diff -Nru peony-4.10.0.5/debian/changelog peony-4.10.0.5/debian/changelog --- peony-4.10.0.5/debian/changelog 2024-12-24 18:28:47.000000000 +0800 +++ peony-4.10.0.5/debian/changelog 2025-03-03 09:26:07.000000000 +0800 @@ -1,3 +1,28 @@ +peony (4.10.0.5-ok2~0303) nile; urgency=medium + + * 修复三岛任务栏gsettings缺失导致的桌面崩溃问题 + + -- Yue Lan <lanyue@kylinos.cn> Mon, 03 Mar 2025 09:26:07 +0800 + +peony (4.10.0.5-ok2~0227) nile; urgency=medium + + * 桌面同步upstream相关改动 + * 移除kscreen相关patch + + -- Yue Lan <lanyue@kylinos.cn> Thu, 27 Feb 2025 17:27:33 +0800 + +peony (4.10.0.5-ok2~0224.1) nile; urgency=medium + + * 删除symbols文件 + + -- Yue Lan <lanyue@kylinos.cn> Mon, 24 Feb 2025 17:30:29 +0800 + +peony (4.10.0.5-ok2~0224) nile; urgency=medium + + * 同步upstream相关改动 + + -- Yue Lan <lanyue@kylinos.cn> Mon, 24 Feb 2025 16:58:50 +0800 + peony (4.10.0.5-ok1~1224) nile; urgency=medium * fix #I9VA55, #I9VP8N diff -Nru peony-4.10.0.5/debian/control peony-4.10.0.5/debian/control --- peony-4.10.0.5/debian/control 2024-12-24 18:28:47.000000000 +0800 +++ peony-4.10.0.5/debian/control 2025-03-03 09:26:07.000000000 +0800 @@ -54,6 +54,7 @@ Multi-Arch: foreign Depends: libpeony3 (= ${binary:Version}), peony-common (= ${source:Version}), + peony-utils (= ${source:Version}), ${misc:Depends}, ${shlibs:Depends} Recommends: gvfs-backends, peony-extensions(>=3.2.2+1015) @@ -115,3 +116,20 @@ . This package contains the development files for the libraries needed by Peony's extensions. + +Package: peony-utils +Section: utils +Architecture: any +Depends: peony (= ${binary:Version}), + gvfs-daemons, + coreutils, + ${misc:Depends}, + ${shlibs:Depends} +Description: utils for Peony + Peony is the official file manager for the UKUI desktop. It allows one + to browse directories, preview files and launch applications associated + with them. It is also responsible for handling the icons on the UKUI + desktop. It works on local and remote filesystems. + . + This package contains several additional utils, for helping peony upgrade + correctly. diff -Nru peony-4.10.0.5/debian/patches/0087-Added-translation-using-Weblate-Arabic.patch peony-4.10.0.5/debian/patches/0087-Added-translation-using-Weblate-Arabic.patch --- peony-4.10.0.5/debian/patches/0087-Added-translation-using-Weblate-Arabic.patch 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/patches/0087-Added-translation-using-Weblate-Arabic.patch 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,567 @@ +From: KevinDuan <duankaiwen@kylinos.cn> +Date: Mon, 17 Feb 2025 09:36:32 +0800 +Subject: Added translation using Weblate (Arabic) + +--- + .../peony-qt-desktop/peony-qt-desktop_ar.ts | 552 +++++++++++++++++++++ + 1 file changed, 552 insertions(+) + create mode 100644 translations/peony-qt-desktop/peony-qt-desktop_ar.ts + +diff --git a/translations/peony-qt-desktop/peony-qt-desktop_ar.ts b/translations/peony-qt-desktop/peony-qt-desktop_ar.ts +new file mode 100644 +index 0000000..ece1695 +--- /dev/null ++++ b/translations/peony-qt-desktop/peony-qt-desktop_ar.ts +@@ -0,0 +1,552 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<!DOCTYPE TS> ++<TS version="2.1" language="ar"> ++<context> ++ <name>Peony::DesktopIconView</name> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-icon-view.h" line="89"/> ++ <source>Desktop Icon View</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="669"/> ++ <source>New Folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>set background</source> ++ <translation type="vanished">设置背景</translation> ++ </message> ++ <message> ++ <source>Delete file Warning</source> ++ <translation type="vanished">删除文件警告</translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="1031"/> ++ <source>Open failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="1032"/> ++ <source>Open directory failed, you have no permission!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="1014"/> ++ <source>Open Link failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="763"/> ++ <source>Set Background</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="1015"/> ++ <source>File not exist, do you want to delete the link file?</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DesktopItemModel</name> ++ <message> ++ <source>Computer</source> ++ <translation type="vanished">计算机</translation> ++ </message> ++ <message> ++ <source>Trash</source> ++ <translation type="vanished">回收站</translation> ++ </message> ++ <message> ++ <source>My Document</source> ++ <translation type="vanished">我的文档</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DesktopMenu</name> ++ <message> ++ <source>&Open in new Window</source> ++ <translation type="vanished">在新窗口中打开(&N)</translation> ++ </message> ++ <message> ++ <source>Select &All</source> ++ <translation type="vanished">全选(&A)</translation> ++ </message> ++ <message> ++ <source>&Open "%1"</source> ++ <translation type="vanished">打开 "%1"(&O)</translation> ++ </message> ++ <message> ++ <source>Open "%1" &with...</source> ++ <translation type="vanished">打开方式(&W)...</translation> ++ </message> ++ <message> ++ <source>&More applications...</source> ++ <translation type="vanished">更多应用(&M)...</translation> ++ </message> ++ <message> ++ <source>Open "%1" with...</source> ++ <translation type="vanished">打开方式...</translation> ++ </message> ++ <message> ++ <source>&Open</source> ++ <translation type="vanished">打开(&O)</translation> ++ </message> ++ <message> ++ <source>Open &with...</source> ++ <translation type="vanished">打开方式(&W)...</translation> ++ </message> ++ <message> ++ <source>&Open %1 selected files</source> ++ <translation type="vanished">打开%1个选中文件(&O)</translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="237"/> ++ <source>Reverse Select</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>&New...</source> ++ <translation type="vanished">新建(&N)</translation> ++ </message> ++ <message> ++ <source>New...</source> ++ <translation type="vanished">新建...</translation> ++ </message> ++ <message> ++ <source>Empty &File</source> ++ <translation type="vanished">空文件(&E)</translation> ++ </message> ++ <message> ++ <source>&Folder</source> ++ <translation type="vanished">文件夹(&F)</translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="355"/> ++ <source>New Folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>View Type...</source> ++ <translation type="vanished">视图类型...</translation> ++ </message> ++ <message> ++ <source>&Small</source> ++ <translation type="vanished">小图标(&S)</translation> ++ </message> ++ <message> ++ <source>&Normal</source> ++ <translation type="vanished">中图标(&N)</translation> ++ </message> ++ <message> ++ <source>&Large</source> ++ <translation type="vanished">大图标(&L)</translation> ++ </message> ++ <message> ++ <source>&Huge</source> ++ <translation type="vanished">超大图标(&H)</translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="109"/> ++ <source>Open in new Window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="114"/> ++ <source>Select All</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="137"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="165"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="199"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="210"/> ++ <source>Open</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Can not open path "%1",permission denied.</source> ++ <translation type="vanished">无法打开路径 "%1", 权限被拒绝。</translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="145"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="170"/> ++ <source>Open with...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="158"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="193"/> ++ <source>More applications...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="213"/> ++ <source>Open %1 selected files</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="251"/> ++ <source>New</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="339"/> ++ <source>Empty File</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="351"/> ++ <source>Folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="374"/> ++ <source>View Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="380"/> ++ <source>Small</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="383"/> ++ <source>Normal</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="386"/> ++ <source>Large</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="389"/> ++ <source>Huge</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="417"/> ++ <source>Sort By</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Sort By...</source> ++ <translation type="vanished">排序方式...</translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="422"/> ++ <source>Name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="424"/> ++ <source>File Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="425"/> ++ <source>File Size</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="497"/> ++ <source>Clean the trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="518"/> ++ <source>Copy</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="545"/> ++ <source>Delete to trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Error</source> ++ <translation type="vanished">错误</translation> ++ </message> ++ <message> ++ <source>Can not show trash properties with other files together!</source> ++ <translation type="vanished">不能将回收站与其他文件一起查看属性!</translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="423"/> ++ <source>Modified Date</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Delete file Warning</source> ++ <translation type="vanished">删除文件警告</translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="524"/> ++ <source>Cut</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>&Delete to trash</source> ++ <translation type="vanished">删除到回收站(&D)</translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="554"/> ++ <source>Delete forever</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="562"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="567"/> ++ <source>Rename</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="574"/> ++ <source>Paste</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="580"/> ++ <source>Refresh</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="594"/> ++ <source>Properties</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>P&roperties</source> ++ <translation type="vanished">属性(&R)</translation> ++ </message> ++ <message> ++ <source>Sort Order...</source> ++ <translation type="vanished">排列顺序...</translation> ++ </message> ++ <message> ++ <source>Ascending Order</source> ++ <translation type="vanished">升序</translation> ++ </message> ++ <message> ++ <source>Descending Order</source> ++ <translation type="vanished">降序</translation> ++ </message> ++ <message> ++ <source>Zoom &In</source> ++ <translation type="vanished">放大(&I)</translation> ++ </message> ++ <message> ++ <source>Zoom &Out</source> ++ <translation type="vanished">缩小(&O)</translation> ++ </message> ++ <message> ++ <source>&Restore all</source> ++ <translation type="vanished">恢复全部(&R)</translation> ++ </message> ++ <message> ++ <source>&Clean the trash</source> ++ <translation type="vanished">清空回收站(&C)</translation> ++ </message> ++ <message> ++ <source>Delete Permanently</source> ++ <translation type="vanished">永久删除</translation> ++ </message> ++ <message> ++ <source>Are you sure that you want to delete these files? Once you start a deletion, the files deleting will never be restored again.</source> ++ <translation type="vanished">您确定要删除这些文件吗?一旦开始删除,这些文件将不可再恢复。</translation> ++ </message> ++ <message> ++ <source>&Copy</source> ++ <translation type="vanished">复制(&C)</translation> ++ </message> ++ <message> ++ <source>Cu&t</source> ++ <translation type="vanished">剪切(&T)</translation> ++ </message> ++ <message> ++ <source>&Delete</source> ++ <translation type="vanished">删除(&D)</translation> ++ </message> ++ <message> ++ <source>&Rename</source> ++ <translation type="vanished">重命名(&R)</translation> ++ </message> ++ <message> ++ <source>&Paste</source> ++ <translation type="vanished">粘贴(&P)</translation> ++ </message> ++ <message> ++ <source>&Refresh</source> ++ <translation type="vanished">刷新(&R)</translation> ++ </message> ++ <message> ++ <source>&Properties</source> ++ <translation type="vanished">属性(&P)</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DesktopModeFactory</name> ++ <message> ++ <source>desktop icon mode</source> ++ <translation type="vanished">桌面图标模式</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DesktopWindow</name> ++ <message> ++ <source>Desktop</source> ++ <translation type="vanished">桌面</translation> ++ </message> ++ <message> ++ <source>set background</source> ++ <translation type="vanished">设置壁纸</translation> ++ </message> ++ <message> ++ <source>New Folder</source> ++ <translation type="vanished">新建文件夹</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::StudyCenterMode</name> ++ <message> ++ <source>math</source> ++ <translation type="vanished">数学</translation> ++ </message> ++ <message> ++ <source>english</source> ++ <translation type="vanished">英语</translation> ++ </message> ++ <message> ++ <source>chinese</source> ++ <translation type="vanished">语文</translation> ++ </message> ++ <message> ++ <source>other</source> ++ <translation type="vanished">其他</translation> ++ </message> ++</context> ++<context> ++ <name>PeonyDesktopApplication</name> ++ <message> ++ <source>Close the peony-qt desktop window</source> ++ <translation type="vanished">关闭桌面并退出</translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="226"/> ++ <source>peony-qt-desktop</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Peony-Qt Desktop</source> ++ <translation type="vanished">桌面</translation> ++ </message> ++ <message> ++ <source>Desktop</source> ++ <translation type="vanished">桌面</translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="560"/> ++ <source>Close the peony desktop window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="563"/> ++ <source>Take over the dbus service.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="566"/> ++ <source>Take over the desktop displaying</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="569"/> ++ <source>Setup backgrounds</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="572"/> ++ <source>Clear standard icons</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Open learning center.</source> ++ <translation type="vanished">打开学习中心</translation> ++ </message> ++ <message> ++ <source>set background</source> ++ <translation type="vanished">设置背景</translation> ++ </message> ++</context> ++<context> ++ <name>QObject</name> ++ <message> ++ <source>set background</source> ++ <translation type="vanished">设置背景</translation> ++ </message> ++ <message> ++ <source>display settings</source> ++ <translation type="vanished">显示设置</translation> ++ </message> ++ <message> ++ <source>set resolution</source> ++ <translation type="vanished">设置分辨率</translation> ++ </message> ++ <message> ++ <source>Error</source> ++ <translation type="vanished">错误</translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktopbackgroundwindow.cpp" line="100"/> ++ <source>Set Background</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktopbackgroundwindow.cpp" line="105"/> ++ <source>Display Settings</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>StudyStatusWidget</name> ++ <message> ++ <source>学情中心</source> ++ <translation type="vanished">学情中心</translation> ++ </message> ++ <message> ++ <source>STATISTICS</source> ++ <translation type="vanished">统计数据</translation> ++ </message> ++ <message> ++ <source>今日使用时长</source> ++ <translation type="vanished">今日使用时长</translation> ++ </message> ++ <message> ++ <source>本周使用时长</source> ++ <translation type="vanished">本周使用时长</translation> ++ </message> ++ <message> ++ <source>本月使用时长</source> ++ <translation type="vanished">本月使用时长</translation> ++ </message> ++ <message> ++ <source>最常使用 (本周累计)</source> ++ <translation type="vanished">最常使用 (本周累计)</translation> ++ </message> ++</context> ++<context> ++ <name>TabletMenu</name> ++ <message> ++ <source>Uninstall</source> ++ <translation type="vanished">卸载</translation> ++ </message> ++</context> ++<context> ++ <name>TabletPluginWidget</name> ++ <message> ++ <source>Search</source> ++ <translation type="vanished">搜索</translation> ++ </message> ++</context> ++</TS> diff -Nru peony-4.10.0.5/debian/patches/0088-Added-translation-using-Weblate-Arabic.patch peony-4.10.0.5/debian/patches/0088-Added-translation-using-Weblate-Arabic.patch --- peony-4.10.0.5/debian/patches/0088-Added-translation-using-Weblate-Arabic.patch 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/patches/0088-Added-translation-using-Weblate-Arabic.patch 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,6887 @@ +From: KevinDuan <duankaiwen@kylinos.cn> +Date: Mon, 17 Feb 2025 09:36:44 +0800 +Subject: Added translation using Weblate (Arabic) + +--- + translations/libpeony-qt/libpeony-qt_ar.ts | 5477 ++++++++++++++++++++++++++++ + translations/peony-qt/peony-qt_ar.ts | 1387 +++++++ + 2 files changed, 6864 insertions(+) + create mode 100644 translations/libpeony-qt/libpeony-qt_ar.ts + create mode 100644 translations/peony-qt/peony-qt_ar.ts + +diff --git a/translations/libpeony-qt/libpeony-qt_ar.ts b/translations/libpeony-qt/libpeony-qt_ar.ts +new file mode 100644 +index 0000000..aee3b52 +--- /dev/null ++++ b/translations/libpeony-qt/libpeony-qt_ar.ts +@@ -0,0 +1,5477 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<!DOCTYPE TS> ++<TS version="2.1" language="ar"> ++<context> ++ <name>ColorPushButton</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/color-pushbutton.cpp" line="52"/> ++ <source>label management ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/color-pushbutton.cpp" line="72"/> ++ <source>Remove "%1"</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>delete "%1"</source> ++ <translation type="vanished">删除“%1”</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/color-pushbutton.cpp" line="92"/> ++ <source>add "%1"</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>ConnectServerDialog</name> ++ <message> ++ <location filename="../../libpeony-qt/connect-server-dialog.ui" line="14"/> ++ <source>Connect to Sever</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-server-dialog.ui" line="32"/> ++ <source>Domain</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-server-dialog.ui" line="39"/> ++ <source>Password</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-server-dialog.ui" line="55"/> ++ <source>Save Password</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-server-dialog.ui" line="62"/> ++ <source>User</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-server-dialog.ui" line="82"/> ++ <source>Anonymous</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-server-dialog.cpp" line="35"/> ++ <source>Ok</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-server-dialog.cpp" line="36"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>DiscControl</name> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/disc/disccontrol.cpp" line="477"/> ++ <source> is busy!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/disc/disccontrol.cpp" line="516"/> ++ <source>is busy!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/disc/disccontrol.cpp" line="563"/> ++ <source> not support udf at present.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/disc/disccontrol.cpp" line="570"/> ++ <source>unmount disc failed before udf format.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>is not properly formatted.</source> ++ <translation type="vanished">格式不正确。</translation> ++ </message> ++ <message> ++ <source>Can not found newfs_udf tool.</source> ++ <translation type="vanished">未找到newfs_udf工具。</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/disc/disccontrol.cpp" line="723"/> ++ <source>DVD+RW udf format fail.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/disc/disccontrol.cpp" line="755"/> ++ <source>preparation failed before DVD-RW udf format.</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>FileLabelModel</name> ++ <message> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="38"/> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="56"/> ++ <source>Red</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="39"/> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="57"/> ++ <source>Orange</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="40"/> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="58"/> ++ <source>Yellow</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="41"/> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="59"/> ++ <source>Green</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="42"/> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="60"/> ++ <source>Blue</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="43"/> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="61"/> ++ <source>Purple</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Gray</source> ++ <translation type="vanished">灰色</translation> ++ </message> ++ <message> ++ <source>Transparent</source> ++ <translation type="vanished">无颜色</translation> ++ </message> ++ <message> ++ <source>Delete file Warning</source> ++ <translation type="vanished">删除文件警告</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="130"/> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="435"/> ++ <source>Error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="130"/> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="435"/> ++ <source>Label or color is duplicated.</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>FileOperationHelper</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-helper.cpp" line="157"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-helper.cpp" line="175"/> ++ <source>Burn failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Format_Dialog</name> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="20"/> ++ <source>Dialog</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="32"/> ++ <source>rom_size</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="45"/> ++ <source>system</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="59"/> ++ <source>vfat/fat32</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="64"/> ++ <source>exfat</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="69"/> ++ <source>ntfs</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>vfat</source> ++ <translation type="vanished">VFAT</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="74"/> ++ <source>ext4</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="88"/> ++ <source>device_name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="114"/> ++ <source>clean it total</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="127"/> ++ <source>ok</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="140"/> ++ <source>close</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="179"/> ++ <source>TextLabel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>qmesg_notify</source> ++ <translation type="vanished">通知</translation> ++ </message> ++ <message> ++ <source>Format operation has been finished successfully.</source> ++ <translation type="vanished">格式化操作已成功完成。</translation> ++ </message> ++ <message> ++ <source>Sorry, the format operation is failed!</source> ++ <translation type="vanished">很遗憾,格式化操作失败了,您可以重新试下!</translation> ++ </message> ++ <message> ++ <source>Formatting this volume will erase all data on it. Please backup all retained data before formatting. Do you want to continue ?</source> ++ <translation type="vanished">格式化此卷将清除其上的所有数据。请在格式化之前备份所有保留的数据。您想继续吗?</translation> ++ </message> ++ <message> ++ <source>format</source> ++ <translation type="vanished">格式化</translation> ++ </message> ++ <message> ++ <source>begin format</source> ++ <translation type="vanished">开始</translation> ++ </message> ++ <message> ++ <source>format_success</source> ++ <translation type="vanished">格式化成功!</translation> ++ </message> ++ <message> ++ <source>format_err</source> ++ <translation type="vanished">格式化失败!</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="154"/> ++ <source>Format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="167"/> ++ <source>Rom size:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="173"/> ++ <source>Filesystem:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="183"/> ++ <source>Disk name:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="213"/> ++ <source>Completely erase(Time is longer, please confirm!)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="231"/> ++ <source>Set password</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="232"/> ++ <source>Set password for volume based on LUKS (only ext4)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="238"/> ++ <source>Formatting to the ext4 file system may cause other users to be unable to read or write to the USB drive</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="246"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="247"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="361"/> ++ <source>Data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="238"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="567"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="567"/> ++ <source>Device name cannot start with a decimal point, Please re-enter!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="578"/> ++ <source>Enter Password:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="596"/> ++ <source>Password too short, please retype a password more than 6 characters</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="695"/> ++ <source>%1/sec, %2 remaining.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="695"/> ++ <source>over one day</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="697"/> ++ <source>getting progress...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1290"/> ++ <source>Error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1290"/> ++ <source>Block not existed!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1337"/> ++ <source>Formatting. Do not close this window</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>KyFileDialogRename</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="97"/> ++ <source>Renaming "%1"</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="98"/> ++ <source>Renaming failed, the reason is: %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="98"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="107"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="116"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="121"/> ++ <source>Filename too long</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="102"/> ++ <source>Copying "%1"</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="106"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="115"/> ++ <source>To "%1"</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="107"/> ++ <source>Copying failed, the reason is: %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="111"/> ++ <source>Moving "%1"</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="116"/> ++ <source>Moving failed, the reason is: %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="120"/> ++ <source>File operation error:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="121"/> ++ <source>The reason is: %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="140"/> ++ <source>Truncation</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="141"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="213"/> ++ <source>Save</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="171"/> ++ <source>All applications</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="174"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="212"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="272"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="175"/> ++ <source>Apply</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="208"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="258"/> ++ <source>Bytes</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="264"/> ++ <source>Front truncation</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="265"/> ++ <source>Post truncation</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="321"/> ++ <source>Description: Skip copying files of the current type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="326"/> ++ <source>truncate interval</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="327"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="345"/> ++ <source>.</source> ++ <translation></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="328"/> ++ <source>Explanation: Truncate the portion of the file name that exceeds 225 bytes and select</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="337"/> ++ <source>Description: By default, save to "%1/扩展".</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="344"/> ++ <source>modify the name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="346"/> ++ <source>Explanation: When renaming a file name, ensure it is within 255 bytes and </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Explanation: When renaming a file name, ensure it is within 225 bytes and </source> ++ <translation type="vanished">说明:用户重命名文件名,保证在225字节以内,去</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="139"/> ++ <source>Skip</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Skip All</source> ++ <translation type="vanished">全部跳过</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="142"/> ++ <source>Rename</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Please enter a new name</source> ++ <translation type="vanished">请输入文件名</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="273"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>MainProgressBar</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="438"/> ++ <source>File operation</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="493"/> ++ <source>starting ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="457"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="598"/> ++ <source>cancel all file operations</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="447"/> ++ <source>Minimize</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="455"/> ++ <source>Close</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="458"/> ++ <source>Are you sure to cancel all file operations?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="599"/> ++ <source>Are you sure want to cancel all file operations</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="460"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="601"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="461"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="602"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="634"/> ++ <source>continue</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="636"/> ++ <source>pause</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="716"/> ++ <source>canceling ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="719"/> ++ <source>sync ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>MessageDialog</name> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1839"/> ++ <source>Peony</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1866"/> ++ <source>Forcibly pulling out the device may cause data ++ loss or device exceptions!</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>OtherButton</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="825"/> ++ <source>Other queue</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::AdvanceSearchBar</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="55"/> ++ <source>Key Words</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="58"/> ++ <source>input key words...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="59"/> ++ <source>Search Location</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="61"/> ++ <source>choose search path...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="68"/> ++ <source>browse</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="69"/> ++ <source>File Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="71"/> ++ <source>Choose File Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="76"/> ++ <source>Modify Time</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="78"/> ++ <source>Choose Modify Time</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="83"/> ++ <source>File Size</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="85"/> ++ <source>Choose file size</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="90"/> ++ <source>show hidden file</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="91"/> ++ <source>go back</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="92"/> ++ <source>hidden advance search page</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="94"/> ++ <source>file name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="95"/> ++ <source>content</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="100"/> ++ <source>search</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="101"/> ++ <source>start search</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="174"/> ++ <source>Select path</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="193"/> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="202"/> ++ <source>Operate Tips</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="194"/> ++ <source>Have no key words or search location!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="203"/> ++ <source>Search file name or content at least choose one!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Search content or file name at least choose one!</source> ++ <translation type="vanished">搜索文件名或者内容请至少指定一个!</translation> ++ </message> ++ <message> ++ <source>all</source> ++ <translation type="vanished">全部</translation> ++ </message> ++ <message> ++ <source>file folder</source> ++ <translation type="vanished">文件夹</translation> ++ </message> ++ <message> ++ <source>image</source> ++ <translation type="vanished">图片</translation> ++ </message> ++ <message> ++ <source>video</source> ++ <translation type="vanished">视频</translation> ++ </message> ++ <message> ++ <source>text file</source> ++ <translation type="vanished">文本</translation> ++ </message> ++ <message> ++ <source>audio</source> ++ <translation type="vanished">音频</translation> ++ </message> ++ <message> ++ <source>others</source> ++ <translation type="vanished">其它</translation> ++ </message> ++ <message> ++ <source>wps file</source> ++ <translation type="vanished">WPS文件</translation> ++ </message> ++ <message> ++ <source>today</source> ++ <translation type="vanished">今天</translation> ++ </message> ++ <message> ++ <source>this week</source> ++ <translation type="vanished">本周</translation> ++ </message> ++ <message> ++ <source>this month</source> ++ <translation type="vanished">本月</translation> ++ </message> ++ <message> ++ <source>this year</source> ++ <translation type="vanished">今年</translation> ++ </message> ++ <message> ++ <source>year ago</source> ++ <translation type="vanished">去年</translation> ++ </message> ++ <message> ++ <source>tiny(0-16K)</source> ++ <translation type="vanished">极小(0-16K)</translation> ++ </message> ++ <message> ++ <source>small(16k-1M)</source> ++ <translation type="vanished">较小(16k-1M)</translation> ++ </message> ++ <message> ++ <source>medium(1M-100M)</source> ++ <translation type="vanished">中等(1M-100M)</translation> ++ </message> ++ <message> ++ <source>big(100M-1G)</source> ++ <translation type="vanished">较大(100M-1G)</translation> ++ </message> ++ <message> ++ <source>large(>1G)</source> ++ <translation type="vanished">极大(>1G)</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::AdvancedLocationBar</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/advanced-location-bar.cpp" line="200"/> ++ <source>Search Content...</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::AdvancedPermissionsPage</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="683"/> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="901"/> ++ <source>Permission refinement settings</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="796"/> ++ <source>Permission refinement settings tip</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="796"/> ++ <source>Setting ACL permissions will result in a change in the user group permissions for basic permissions. Do you need to continue setting ACL permissions?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="828"/> ++ <source>User</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="828"/> ++ <source>Read</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="828"/> ++ <source>Write</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="828"/> ++ <source>Executable</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="913"/> ++ <source>delete</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="915"/> ++ <source>Inherit permission</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="930"/> ++ <source>Add</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="947"/> ++ <source>Apply</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="948"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::AllFileLaunchDialog</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="380"/> ++ <source>Choose new application</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="382"/> ++ <source>Choose an Application to open this file</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="389"/> ++ <source>apply now</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="395"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="396"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::AudioPlayManager</name> ++ <message> ++ <source>Operation file Warning</source> ++ <translation type="vanished">文件操作警告</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::BasicPropertiesPage</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="935"/> ++ <source>Choose a custom icon</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="271"/> ++ <source>Type:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Display Name:</source> ++ <translation type="vanished">名称:</translation> ++ </message> ++ <message> ++ <source>Location:</source> ++ <translation type="vanished">路径:</translation> ++ </message> ++ <message> ++ <source>Overview:</source> ++ <translation type="vanished">概览:</translation> ++ </message> ++ <message> ++ <source>Change</source> ++ <translation type="vanished">更改</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="243"/> ++ <source>Name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="244"/> ++ <source>Location</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>move</source> ++ <translation type="vanished">移动</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="451"/> ++ <source>symbolLink</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="455"/> ++ <source>Folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="278"/> ++ <source>Include:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="282"/> ++ <source>Open with:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="286"/> ++ <source>Description:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="289"/> ++ <source>Select multiple files</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="277"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="295"/> ++ <source>Size:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Total size:</source> ++ <translation type="vanished">实际大小:</translation> ++ </message> ++ <message> ++ <source>Space Useage:</source> ++ <translation type="vanished">占用空间:</translation> ++ </message> ++ <message> ++ <source>yyyy-MM-dd, HH:mm:ss</source> ++ <translation type="vanished">yyyy年MM月dd日, HH:mm:ss</translation> ++ </message> ++ <message> ++ <source>yyyy-MM-dd, hh:mm:ss AP</source> ++ <translation type="vanished">yyyy年MM月dd日, hh:mm:ss AP</translation> ++ </message> ++ <message> ++ <source>Time Created:</source> ++ <translation type="vanished">创建时间:</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="296"/> ++ <source>Space Usage:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="307"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="315"/> ++ <source>Time Create:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="308"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="316"/> ++ <source>Time Modified:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="309"/> ++ <source>Time Access:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="322"/> ++ <source>Readonly</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="323"/> ++ <source>Hidden</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="332"/> ++ <source>Property:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="614"/> ++ <source>usershare</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="748"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="989"/> ++ <source>%1 (%2 Bytes)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="943"/> ++ <source>Please select a image that is smaller than 1MB.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Choose a new folder:</source> ++ <translation type="vanished">选择一个新的文件夹:</translation> ++ </message> ++ <message> ++ <source>Error</source> ++ <translation type="vanished">错误</translation> ++ </message> ++ <message> ++ <source>cannot move a folder to itself !</source> ++ <translation type="vanished">不能移动一个文件夹到它内部!</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="982"/> ++ <source>%1 Bytes</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>%1 KB (%2 Bytes)</source> ++ <translation type="vanished">%1 KB (%2 字节)</translation> ++ </message> ++ <message> ++ <source>%1 MB (%2 Bytes)</source> ++ <translation type="vanished">%1 MB (%2 字节)</translation> ++ </message> ++ <message> ++ <source>%1 GB (%2 Bytes)</source> ++ <translation type="vanished">%1 GB (%2 字节)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1002"/> ++ <source>%1 files, %2 folders</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1118"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1120"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1125"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1127"/> ++ <source>Can't get remote file information</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>%1 files (include root files), %2 hidden</source> ++ <translation type="vanished">共%1个文件(包括顶层目录),有%2个隐藏文件</translation> ++ </message> ++ <message> ++ <source>%1 total</source> ++ <translation type="vanished">共%1</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::ComputerPropertiesPage</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="102"/> ++ <source>CPU Name:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="103"/> ++ <source>CPU Core:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="104"/> ++ <source>Memory Size:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="116"/> ++ <source>User Name: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="117"/> ++ <source>Desktop: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="125"/> ++ <source>You should mount this volume first</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="142"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="240"/> ++ <source>Name: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="142"/> ++ <source>File System</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="142"/> ++ <source>Data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="143"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="245"/> ++ <source>Total Space: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="144"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="246"/> ++ <source>Used Space: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="145"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="247"/> ++ <source>Free Space: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="146"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="249"/> ++ <source>Type: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="262"/> ++ <source>Kylin Burner</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="268"/> ++ <source>Open with: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="275"/> ++ <source>Unknown</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::ConnectServerDialog</name> ++ <message> ++ <source>connect to server</source> ++ <translation type="vanished">连接服务器</translation> ++ </message> ++ <message> ++ <source>ip</source> ++ <translation type="vanished">服务器</translation> ++ </message> ++ <message> ++ <source>port</source> ++ <translation type="vanished">端口</translation> ++ </message> ++ <message> ++ <source>type</source> ++ <translation type="vanished">类型</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="143"/> ++ <source>Connect to server</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="168"/> ++ <source>Ip</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="170"/> ++ <source>Port</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="171"/> ++ <source>Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="202"/> ++ <source>Personal Collection server:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="215"/> ++ <source>Add</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="216"/> ++ <source>Delete</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="217"/> ++ <source>Connect</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="296"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="387"/> ++ <source>Ip input error, please re-enter!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="300"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="391"/> ++ <source>Port input error, please re-enter!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>add</source> ++ <translation type="vanished">添加</translation> ++ </message> ++ <message> ++ <source>delete</source> ++ <translatorcomment>连接</translatorcomment> ++ <translation type="vanished">删除</translation> ++ </message> ++ <message> ++ <source>connect</source> ++ <translation type="vanished">连接</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="296"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="300"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="387"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="391"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>ip input error, please re-enter!</source> ++ <translation type="vanished">IP输入错误, 请重新输入!</translation> ++ </message> ++ <message> ++ <source>port input error, please re-enter!</source> ++ <translation type="vanished">端口号输入错误, 请重新输入!</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::ConnectServerLogin</name> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="487"/> ++ <source>The login user</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="496"/> ++ <source>Please enter the %1's user name and password of the server.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="503"/> ++ <source>User's identity</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="504"/> ++ <source>Guest</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="520"/> ++ <source>Name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="521"/> ++ <source>Password</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="542"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="543"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>guest</source> ++ <translation type="vanished">游客(匿名登录)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="505"/> ++ <source>Registered users</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>name</source> ++ <translation type="vanished">用户名</translation> ++ </message> ++ <message> ++ <source>password</source> ++ <translation type="vanished">密码</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="522"/> ++ <source>Remember the password</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>cancel</source> ++ <translation type="vanished">取消</translation> ++ </message> ++ <message> ++ <source>ok</source> ++ <translation type="vanished">连接</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::CreateLinkInternalPlugin</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="132"/> ++ <source>Create Link to Desktop</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="158"/> ++ <source>Create Link to...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="161"/> ++ <source>Choose a Directory to Create Link</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Peony-Qt Create Link Extension</source> ++ <translation type="vanished">创建链接</translation> ++ </message> ++ <message> ++ <source>Create Link Menu Extension.</source> ++ <translation type="vanished">创建链接.</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::CreateSharedFileLinkMenuPlugin</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="271"/> ++ <source>Create Link to Desktop</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::CreateTemplateOperation</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="76"/> ++ <source>NewFile</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="92"/> ++ <source>Create file</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="104"/> ++ <source>NewFolder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="123"/> ++ <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="156"/> ++ <source>Create file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::CustomErrorHandler</name> ++ <message> ++ <location filename="../../libpeony-qt/custom-error-handler.cpp" line="43"/> ++ <source>Is Error Handled?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/custom-error-handler.cpp" line="48"/> ++ <source>Error not be handled correctly</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DefaultAcitonWidget</name> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="531"/> ++ <source>No default app</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DefaultOpenWithWidget</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="439"/> ++ <source>No default app</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DefaultPreviewPage</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="75"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="216"/> ++ <source>Select the file you want to preview...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="207"/> ++ <source>Can not preview this file.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Can not preivew this file.</source> ++ <translation type="vanished">不能预览该文件</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DefaultPreviewPageFactory</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page-factory.h" line="50"/> ++ <source>Default Preview</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page-factory.h" line="53"/> ++ <source>This is the Default Preview of peony-qt</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DetailsPropertiesPage</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="181"/> ++ <source>Name:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="184"/> ++ <source>File type:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="200"/> ++ <source>Location:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="211"/> ++ <source>yyyy-MM-dd, HH:mm:ss</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="204"/> ++ <source>Create time:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="208"/> ++ <source>Modify time:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>yyyy-MM-dd, hh:mm:ss AP</source> ++ <translation type="vanished">yyyy年MM月dd日, hh:mm:ss AP</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="232"/> ++ <source>File size:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="239"/> ++ <source>Width:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="242"/> ++ <source>Height:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="250"/> ++ <source>Owner</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="251"/> ++ <source>Owner:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="253"/> ++ <source>Computer</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="254"/> ++ <source>Computer:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="309"/> ++ <source>%1 (this computer)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="316"/> ++ <source>Unknown</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="358"/> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="359"/> ++ <source>Can't get remote file information</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="368"/> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="369"/> ++ <source>%1 px</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DirectoryView::IconView</name> ++ <message> ++ <source>Icon View</source> ++ <translation type="vanished">图标视图</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/directory-view/view/icon-view/icon-view.cpp" line="334"/> ++ <source>warn</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/directory-view/view/icon-view/icon-view.cpp" line="334"/> ++ <source>This operation is not supported.</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DirectoryView::IconView2</name> ++ <message> ++ <source>Icon View</source> ++ <translation type="vanished">图标视图</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DirectoryView::ListView</name> ++ <message> ++ <source>List View</source> ++ <translation type="vanished">列表视图</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/directory-view/view/list-view/list-view.cpp" line="605"/> ++ <source>warn</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/directory-view/view/list-view/list-view.cpp" line="605"/> ++ <source>This operation is not supported.</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DirectoryView::ListView2</name> ++ <message> ++ <source>List View</source> ++ <translation type="vanished">列表视图</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DirectoryViewFactoryManager</name> ++ <message> ++ <source>Icon View</source> ++ <translation type="vanished">图标视图</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DirectoryViewMenu</name> ++ <message> ++ <source>Open in &New Window</source> ++ <translation type="vanished">在新窗口中打开(&N)</translation> ++ </message> ++ <message> ++ <source>Open in New &Tab</source> ++ <translation type="vanished">在新标签页中打开(&T)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="360"/> ++ <source>Add to bookmark</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>&Open "%1"</source> ++ <translation type="vanished">打开“%1”(&O)</translation> ++ </message> ++ <message> ++ <source>Open "%1" in &New Window</source> ++ <translation type="vanished">在新窗口中打开“%1”(&N)</translation> ++ </message> ++ <message> ++ <source>Open "%1" in New &Tab</source> ++ <translation type="vanished">在新标签页中打开“%1”(&T)</translation> ++ </message> ++ <message> ++ <source>Open "%1" with...</source> ++ <translation type="vanished">选用其它应用打开“%1”...</translation> ++ </message> ++ <message> ++ <source>&More applications...</source> ++ <translation type="vanished">更多应用...(&M)</translation> ++ </message> ++ <message> ++ <source>&Open</source> ++ <translation type="vanished">打开(&O)</translation> ++ </message> ++ <message> ++ <source>Open &with...</source> ++ <translation type="vanished">打开方式(&W)...</translation> ++ </message> ++ <message> ++ <source>&Open %1 selected files</source> ++ <translation type="vanished">打开%1个选中文件(&O)</translation> ++ </message> ++ <message> ++ <source>&New...</source> ++ <translation type="vanished">新建...(&N)</translation> ++ </message> ++ <message> ++ <source>Empty &File</source> ++ <translation type="vanished">空文件(&E)</translation> ++ </message> ++ <message> ++ <source>&Folder</source> ++ <translation type="vanished">文件夹(&F)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="691"/> ++ <source>New Folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Icon View</source> ++ <translation type="vanished">图标视图</translation> ++ </message> ++ <message> ++ <source>List View</source> ++ <translation type="vanished">列表视图</translation> ++ </message> ++ <message> ++ <source>View Type...</source> ++ <translation type="vanished">视图类型...</translation> ++ </message> ++ <message> ++ <source>Sort By...</source> ++ <translation type="vanished">排序类型...</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="743"/> ++ <source>Name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="745"/> ++ <source>File Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="746"/> ++ <source>File Size</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>New...</source> ++ <translation type="vanished">新建...</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="322"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="413"/> ++ <source>Open in New Window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="332"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="423"/> ++ <source>Open in New Tab</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="380"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="435"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="499"/> ++ <source>Open</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="391"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="451"/> ++ <source>Open with...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="406"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="488"/> ++ <source>More applications...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="508"/> ++ <source>Open %1 selected files</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="556"/> ++ <source>New</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="675"/> ++ <source>Empty File</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="687"/> ++ <source>Folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="717"/> ++ <source>View Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="737"/> ++ <source>Sort By</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="744"/> ++ <source>Modified Date</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="747"/> ++ <source>Original Path</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Sort Order...</source> ++ <translation type="vanished">排序顺序...</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="780"/> ++ <source>Ascending Order</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1470"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1480"/> ++ <source>Peony-Qt Filesafe Menu Extension</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1512"/> ++ <source>MultiSelect</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="779"/> ++ <source>Descending Order</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Sort Preferences...</source> ++ <translation type="vanished">排序偏好...</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="798"/> ++ <source>Folder First</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="807"/> ++ <source>Chinese First</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="816"/> ++ <source>Show Hidden</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="851"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="859"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1039"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1313"/> ++ <source>Copy</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1373"/> ++ <source>File:"%1" is not exist, did you moved or deleted it?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Peony-Qt filesafe menu Extension</source> ++ <translation type="vanished">文件保护箱扩展</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1470"/> ++ <source>Peony File Labels Menu Extension</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>&Copy</source> ++ <translation type="vanished">复制(&C)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="889"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1318"/> ++ <source>Cut</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="916"/> ++ <source>Delete to trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="983"/> ++ <source>Paste</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1052"/> ++ <source>Refresh</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1063"/> ++ <source>Select All</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1107"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1168"/> ++ <source>Properties</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1209"/> ++ <source>format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1263"/> ++ <source>Restore</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="928"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1033"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1293"/> ++ <source>Delete</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="772"/> ++ <source>Sort Order</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="793"/> ++ <source>Sort Preferences</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1372"/> ++ <source>Error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>File:"%1 is not exist, did you moved or deleted it?</source> ++ <translation type="vanished">文件:"%1" 不存在,您是否已经移动或者删除了它?</translation> ++ </message> ++ <message> ++ <source>File original path not exist, are you deleted or moved it?</source> ++ <translation type="vanished">文件原始路径未找到,您是否已经移动或删除了它?</translation> ++ </message> ++ <message> ++ <source>Cu&t</source> ++ <translation type="vanished">剪切(&T)</translation> ++ </message> ++ <message> ++ <source>&Delete to trash</source> ++ <translation type="vanished">删除到回收站(&D)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="931"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="942"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="950"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="959"/> ++ <source>Delete forever</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="966"/> ++ <source>Rename</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Select &All</source> ++ <translation type="vanished">全选(&A)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1072"/> ++ <source>Reverse Select</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>P&roperties</source> ++ <translation type="vanished">属性(&R)</translation> ++ </message> ++ <message> ++ <source>&Delete</source> ++ <translation type="vanished">删除(&D)</translation> ++ </message> ++ <message> ++ <source>&Rename</source> ++ <translation type="vanished">重命名(&R)</translation> ++ </message> ++ <message> ++ <source>&Paste</source> ++ <translation type="vanished">粘贴(&P)</translation> ++ </message> ++ <message> ++ <source>&Refresh</source> ++ <translation type="vanished">刷新(&R)</translation> ++ </message> ++ <message> ++ <source>&Properties</source> ++ <translation type="vanished">属性(&P)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1236"/> ++ <source>&Clean the Trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Delete file Warning</source> ++ <translation type="vanished">删除文件警告</translation> ++ </message> ++ <message> ++ <source>Delete Permanently</source> ++ <translation type="vanished">永久删除</translation> ++ </message> ++ <message> ++ <source>Are you sure that you want to delete these files? Once you start a deletion, the files deleting will never be restored again.</source> ++ <translation type="vanished">您确定要删除这些文件吗?一旦开始删除,这些文件将不可再恢复。</translation> ++ </message> ++ <message> ++ <source>&Restore</source> ++ <translation type="vanished">还原(&R)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1326"/> ++ <source>Clean All</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1343"/> ++ <source>Open Parent Folder in New Window</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DirectoryViewWidget</name> ++ <message> ++ <source>Directory View</source> ++ <translation type="vanished">文件视图</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::ExtensionsManagerWidget</name> ++ <message> ++ <location filename="../../libpeony-qt/extensions-manager-widget.cpp" line="64"/> ++ <source>Extensions Manager</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/extensions-manager-widget.cpp" line="68"/> ++ <source>Available extensions</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/extensions-manager-widget.cpp" line="70"/> ++ <source>Ok</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/extensions-manager-widget.cpp" line="71"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FMWindow</name> ++ <message> ++ <location filename="../../libpeony-qt/windows/fm-window.cpp" line="93"/> ++ <source>File Manager</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/fm-window.cpp" line="171"/> ++ <source>advanced search</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/fm-window.cpp" line="174"/> ++ <source>clear record</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/fm-window.cpp" line="279"/> ++ <source>Loaing... Press Esc to stop a loading.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/fm-window.cpp" line="395"/> ++ <source>Author: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019-2020, Tianjin KYLIN Information Technology Co., Ltd.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Author: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019-2020, Tianjin KYLIN Information Technology Co., Ltd.</source> ++ <translation type="vanished">作者: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++版权所有(C): 2019-2020,天津麒麟信息技术有限公司.</translation> ++ </message> ++ <message> ++ <source>Ctrl+H</source> ++ <comment>Show|Hidden</comment> ++ <translation type="vanished">Ctrl+H</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/fm-window.cpp" line="324"/> ++ <source>Undo</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/fm-window.cpp" line="331"/> ++ <source>Redo</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/fm-window.cpp" line="394"/> ++ <source>Peony Qt</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Authour: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019-2020, Tianjin KYLIN Information Technology Co., Ltd.</source> ++ <translation type="vanished">作者: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++版权所有(C): 2019-2020,天津麒麟信息技术有限公司.</translation> ++ </message> ++ <message> ++ <source>Authour: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> ++ <translation type="vanished">作者: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++版权所有(C): 2019,天津麒麟信息技术有限公司.</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/fm-window.cpp" line="448"/> ++ <source>New Folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileBatchRenameOperation</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-batch-rename-operation.cpp" line="75"/> ++ <source>File Rename error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-batch-rename-operation.cpp" line="76"/> ++ <source>Invalid file name %1%2%3 .</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-batch-rename-operation.cpp" line="91"/> ++ <source>File Rename warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-batch-rename-operation.cpp" line="92"/> ++ <source>Are you sure to hidden these files?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-batch-rename-operation.cpp" line="206"/> ++ <location filename="../../libpeony-qt/file-operation/file-batch-rename-operation.cpp" line="241"/> ++ <source>Rename file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileCopy</name> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="173"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="181"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="201"/> ++ <source>Error in source or destination file path!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="190"/> ++ <source>Error when copy file: %1, can not copy special files, skip this file and continue?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="213"/> ++ <source>Can not copy %1, file doesn't exist. Has the file been renamed or moved?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="235"/> ++ <source>The dest file "%1" has existed!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="263"/> ++ <source>Vfat/FAT32 file systems do not support a single file that occupies more than 4 GB space!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="289"/> ++ <source>Error writing to file: Input/output error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="476"/> ++ <source>Failed to create %1. Please ensure if it is in root directory, or if the device supports gphoto2 protocol correctly.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="482"/> ++ <source>Failed to create %1.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Error opening source or destination file!</source> ++ <translation type="vanished">打开源文件或者目标文件出错!</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="391"/> ++ <source>Please check whether the device has been removed!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="393"/> ++ <source>Write file error: There is no available disk space for device!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Please confirm that the device controls are insufficient!</source> ++ <translation type="vanished">请确认设备空间是否足够!</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="470"/> ++ <source>File opening failure</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Reading and Writing files are inconsistent!</source> ++ <translation type="vanished">读和写文件不一致!</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="280"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="408"/> ++ <source>operation cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileCopyOperation</name> ++ <message> ++ <source>File copy</source> ++ <translation type="vanished">文件复制</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="228"/> ++ <source>Create folder %1 failed: %2</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="232"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="621"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1078"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1139"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1317"/> ++ <source>File copy error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="246"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="273"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="646"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="670"/> ++ <source>The file name exceeds the limit</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="629"/> ++ <source>Cannot opening file, permission denied!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="631"/> ++ <source>File:%1 was not found.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1065"/> ++ <source>File System</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1067"/> ++ <source>Data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1073"/> ++ <source>%1 no space left on device. Copy file size: %2 GB, Space needed: %3 GB.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1185"/> ++ <source>Link file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1319"/> ++ <source>Burning does not support replacement</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Burn failed</source> ++ <translation type="vanished">刻录失败</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileDeleteOperation</name> ++ <message> ++ <source>File delete</source> ++ <translation type="vanished">文件删除</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-delete-operation.cpp" line="84"/> ++ <location filename="../../libpeony-qt/file-operation/file-delete-operation.cpp" line="110"/> ++ <source>File delete error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-delete-operation.cpp" line="147"/> ++ <source>Delete file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-delete-operation.cpp" line="150"/> ++ <source>Invalid Operation! Can not delete "%1".</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileEnumerator</name> ++ <message> ++ <source>Delete file Warning</source> ++ <translation type="vanished">删除文件警告</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-enumerator.cpp" line="585"/> ++ <source>The password dialog box is canceled</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-enumerator.cpp" line="587"/> ++ <source>Message recipient disconnected from message bus without replying!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-enumerator.cpp" line="589"/> ++ <source>Error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Did not find target path, do you move or deleted it?</source> ++ <translation type="vanished">未找到目标路径,您是否已经移动或删除了它?</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileInfo</name> ++ <message> ++ <location filename="../../libpeony-qt/file-info.cpp" line="309"/> ++ <source>data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-info.cpp" line="444"/> ++ <source>folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-info.cpp" line="448"/> ++ <location filename="../../libpeony-qt/file-info.cpp" line="456"/> ++ <location filename="../../libpeony-qt/file-info.cpp" line="458"/> ++ <source>file</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-info.cpp" line="451"/> ++ <source>text file</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileInfoJob</name> ++ <message> ++ <source>Trash</source> ++ <translation type="vanished">回收站</translation> ++ </message> ++ <message> ++ <source>Computer</source> ++ <translation type="vanished">计算机</translation> ++ </message> ++ <message> ++ <source>Network</source> ++ <translation type="vanished">网络</translation> ++ </message> ++ <message> ++ <source>Recent</source> ++ <translation type="vanished">最近</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileInformationLabel</name> ++ <message> ++ <source>File location:</source> ++ <translation type="vanished">文件位置:</translation> ++ </message> ++ <message> ++ <source>File size:</source> ++ <translation type="vanished">文件大小:</translation> ++ </message> ++ <message> ++ <source>Modify time:</source> ++ <translation type="vanished">修改时间:</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileInfosJob</name> ++ <message> ++ <source>Computer</source> ++ <translation type="vanished">计算机</translation> ++ </message> ++ <message> ++ <source>Network</source> ++ <translation type="vanished">网络</translation> ++ </message> ++ <message> ++ <source>Recent</source> ++ <translation type="vanished">最近</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileItem</name> ++ <message> ++ <source>Delete file Warning</source> ++ <translation type="vanished">删除文件警告</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="251"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="253"/> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="326"/> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="338"/> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="346"/> ++ <source>Error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="313"/> ++ <source>Open Link failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="314"/> ++ <source>File not exist, do you want to delete the link file?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="327"/> ++ <source>Can not open path "%1",permission denied.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="337"/> ++ <source>Can not find path "%1",are you moved or renamed it?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Can not find path "%1" .</source> ++ <translation type="vanished">找不到路径: "%1" 。</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileItemModel</name> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="357"/> ++ <source>child(ren)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="344"/> ++ <source>Symbol Link, </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="403"/> ++ <source>File Name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="407"/> ++ <source>Delete Date</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="409"/> ++ <source>Create Date</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="416"/> ++ <source>File Size</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="418"/> ++ <source>Original Path</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="414"/> ++ <source>File Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="412"/> ++ <source>Modified Date</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileLabelInternalMenuPlugin</name> ++ <message> ++ <source>Add File Label...</source> ++ <translation type="vanished">添加标记...</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="202"/> ++ <source>Add File Label</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="225"/> ++ <source>Delete All Label</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Peony File Labels Menu Extension</source> ++ <translation type="vanished">文件标记</translation> ++ </message> ++ <message> ++ <source>Tag a File with Menu.</source> ++ <translation type="vanished">菜单中增加标记功能.</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="237"/> ++ <source>label management ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileLabelWidget</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="422"/> ++ <source>label management ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileLauchDialog</name> ++ <message> ++ <source>Applications</source> ++ <translation type="vanished">应用程序</translation> ++ </message> ++ <message> ++ <source>Choose an Application to open this file</source> ++ <translation type="vanished">选择一个应用打开这个文件</translation> ++ </message> ++ <message> ++ <source>Set as Default</source> ++ <translation type="vanished">设为默认</translation> ++ </message> ++ <message> ++ <source>OK</source> ++ <translation type="vanished">确定</translation> ++ </message> ++ <message> ++ <source>No application is set to open file %1</source> ++ <translation type="vanished">未设定用来打开文件“%1”的应用程序。</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="146"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="162"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="177"/> ++ <source>The opening mode of the %1 %2</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="146"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="147"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="177"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="178"/> ++ <source>unknown</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="147"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="178"/> ++ <source>No application is set to open file "%1 %2"</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="152"/> ++ <source>Still using the last opened application:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="162"/> ++ <source>known</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="165"/> ++ <source>Open application is used by default:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="186"/> ++ <source>You can search in the Software Center for an application that can open this file, or select an existing application on your computer.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="208"/> ++ <source>Other application:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="210"/> ++ <source>Select application:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="256"/> ++ <source>Always open the %1%2 file with this application</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="279"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="286"/> ++ <source>Choose other application</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="280"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="293"/> ++ <source>Go to application center</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="351"/> ++ <source>Ok</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="352"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="421"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="412"/> ++ <source>Desktop files(*.desktop)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="419"/> ++ <source>Select Open Action</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="420"/> ++ <source>Select</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileLaunchAction</name> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="215"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="309"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="496"/> ++ <source>Execute Directly</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="216"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="310"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="497"/> ++ <source>Execute in Terminal</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="219"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="314"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="501"/> ++ <source>Detected launching an executable file %1, you want?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Delete file Warning</source> ++ <translation type="vanished">删除文件警告</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="255"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="366"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="535"/> ++ <source>Open Failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="255"/> ++ <source>Can not open %1, file not exist, is it deleted?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>File not exist, is it deleted or moved to other path?</source> ++ <translation type="vanished">文件不存在,您是否已将其删除或挪动位置?</translation> ++ </message> ++ <message> ++ <source>Can not open %1</source> ++ <translation type="vanished">不能打开%1</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="189"/> ++ <source>No Permission</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="189"/> ++ <source>File is not readable. Please check if file has read permisson.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="308"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="495"/> ++ <source>By Default App</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="313"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="500"/> ++ <source>Launch Options</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="355"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="524"/> ++ <source>Open Link failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="356"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="525"/> ++ <source>File not exist, do you want to delete the link file?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="367"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="536"/> ++ <source>Can not open %1, Please confirm you have the right authority.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="371"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="541"/> ++ <source>Open App failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="372"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="542"/> ++ <source>The linked app is changed or uninstalled, so it can not work correctly. ++Do you want to delete the link file?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="386"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="391"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="553"/> ++ <source>Error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="387"/> ++ <source>File original path not exist, are you deleted or moved it?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="391"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="553"/> ++ <source>Can not get a default application for opening %1, do you want open it with text format?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Warning</source> ++ <translation type="vanished">警告</translation> ++ </message> ++ <message> ++ <source>Can not open the file, application is disabled</source> ++ <translation type="vanished">无法打开文件,应用被禁用。</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileLinkOperation</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-link-operation.cpp" line="46"/> ++ <location filename="../../libpeony-qt/file-operation/file-link-operation.cpp" line="49"/> ++ <source>Symbolic Link</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-link-operation.cpp" line="89"/> ++ <source>Link file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Link file</source> ++ <translation type="vanished">创建文件链接</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileMoveOperation</name> ++ <message> ++ <source>Invalid move operation, cannot move a file itself.</source> ++ <translation type="vanished">非法的移动操作,不能自移动到自身。</translation> ++ </message> ++ <message> ++ <source>Move file</source> ++ <translation type="vanished">文件移动</translation> ++ </message> ++ <message> ++ <source>Create file</source> ++ <translation type="vanished">文件创建</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="186"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="414"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="534"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="863"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1768"/> ++ <source>Move file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="301"/> ++ <source>File System</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="303"/> ++ <source>Data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="309"/> ++ <source>%1 no space left on device. Copy file size: %2 GB, Space needed: %3 GB.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="314"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1944"/> ++ <source>File move error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="827"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="837"/> ++ <source>Invalid move operation, cannot move a file into its sub directories.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="881"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="912"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="965"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1234"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1258"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1389"/> ++ <source>The file name exceeds the limit</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1211"/> ++ <source>Create file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1217"/> ++ <source>Cannot opening file, permission denied!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1219"/> ++ <source>File:%1 was not found.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="828"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1675"/> ++ <source>Invalid Operation.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1735"/> ++ <source>File delete error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1814"/> ++ <source>Link file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1946"/> ++ <source>Burning does not support replacement</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Burn failed</source> ++ <translation type="vanished">刻录失败</translation> ++ </message> ++ <message> ++ <source>File delete</source> ++ <translation type="vanished">文件删除</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="835"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1737"/> ++ <source>Invalid Operation</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationAfterProgressPage</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="362"/> ++ <source>&More Details</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationErrorDialog</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="45"/> ++ <source>File Operation Error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="53"/> ++ <source>unkwon</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="54"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="55"/> ++ <source>null</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="57"/> ++ <source>Error message:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="58"/> ++ <source>Source File:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="59"/> ++ <source>Dest File:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="63"/> ++ <source>Ignore</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="64"/> ++ <source>Ignore All</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="65"/> ++ <source>Overwrite</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="66"/> ++ <source>Overwrite All</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="67"/> ++ <source>Backup</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="68"/> ++ <source>Backup All</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="69"/> ++ <source>&Retry</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="70"/> ++ <source>&Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationErrorDialogBase</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog-base.cpp" line="82"/> ++ <source>Close</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationErrorDialogConflict</name> ++ <message> ++ <source>This location already contains a file with the same name.</source> ++ <translation type="vanished">目标文件夹里已经包含有同名文件</translation> ++ </message> ++ <message> ++ <source>Please select the file to keep</source> ++ <translation type="vanished">请选择要保留的文件</translation> ++ </message> ++ <message> ++ <source>This location already contains the file,</source> ++ <translation type="vanished">这里已包含此文件</translation> ++ </message> ++ <message> ++ <source>Do you want to override it?</source> ++ <translation type="vanished">你确定要覆盖它吗</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="54"/> ++ <source>Replace</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="63"/> ++ <source>Ignore</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="82"/> ++ <source>Do the same</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="105"/> ++ <source><p>This location already contains the file '%1', Do you want to override it?</p></source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="111"/> ++ <source>Unexpected error from %1 to %2</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Then do the same thing in a similar situation</source> ++ <translation type="vanished">之后类似情况执行相同操作</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="72"/> ++ <source>Backup</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Cancel</source> ++ <translation type="vanished">取消</translation> ++ </message> ++ <message> ++ <source>OK</source> ++ <translation type="vanished">确定</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationErrorDialogNotSupported</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="320"/> ++ <source>Yes</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="312"/> ++ <source>No</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Cancel</source> ++ <translation type="vanished">取消</translation> ++ </message> ++ <message> ++ <source>Do the same</source> ++ <translation type="vanished">全部应用</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="357"/> ++ <source>Make sure the disk is not full or write protected and that the file is not protected</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationErrorDialogWarning</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="204"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="213"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="245"/> ++ <source>Make sure the disk is not full or write protected and that the file is not protected</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Please make sure the disk is not full or not is write protected, or file is not being used.</source> ++ <translation type="vanished">请确保磁盘未满或未被写保护或未被使用。</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationInfo</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="1075"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="1077"/> ++ <source>Symbolic Link</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source> - Symbolic Link</source> ++ <translation type="vanished">-快捷方式</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationManager</name> ++ <message> ++ <source>Delete file Warning</source> ++ <translation type="vanished">删除文件警告</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="244"/> ++ <source>Warn</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="244"/> ++ <source>'%1' is occupied,you cannot operate!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>No, go to settings</source> ++ <translation type="vanished">否,跳转到设置</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="262"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="266"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="271"/> ++ <source>Do you want to put selected %1 item(s) into trash?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="273"/> ++ <source>Do not show again</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="440"/> ++ <source>File System</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="442"/> ++ <source>Data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="446"/> ++ <source>Insufficient storage space</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="449"/> ++ <source>%1 no space left on device. Copy file size: %2 GB, Space needed: %3 GB.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="491"/> ++ <source>Can't delete.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="492"/> ++ <source>You can't delete a file whenthe file is doing another operation</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="613"/> ++ <source>File Operation is Busy</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="614"/> ++ <source>There have been one or more fileoperation(s) executing before. Youroperation will wait for executinguntil it/them done. If you really want to execute file operations parallelly anyway, you can change the default option "Allow Parallel" in option menu.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="640"/> ++ <source>The long name file is saved to %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>The system cannot hibernate or sleep</source> ++ <translation type="vanished">无法进入休眠或睡眠模式</translation> ++ </message> ++ <message> ++ <source>The file operation is in progress. Ensure that the file operation is complete or canceled before hibernating or sleeping</source> ++ <translation type="vanished">文件操作进行中,\ ++进入休眠或者睡眠之前,请先确保文件操作已完成或者取消</translation> ++ </message> ++ <message> ++ <source>There have been one or more fileoperation(s) executing before. Youroperation will wait for executinguntil it/them done.</source> ++ <translation type="vanished">在执行该操作之前有操作未完成,它需要等待上一个操作完成后再执行。</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationPreparePage</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="298"/> ++ <source>counting:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="299"/> ++ <source>state:</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationProgressPage</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="321"/> ++ <source>&More Details</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="332"/> ++ <source>From:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="333"/> ++ <source>To:</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationProgressWizard</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="55"/> ++ <source>File Manager</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="59"/> ++ <source>&Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="68"/> ++ <source>Preparing...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="71"/> ++ <source>Handling...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="74"/> ++ <source>Clearing...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="77"/> ++ <source>Rollbacking...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="81"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="94"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="120"/> ++ <source>File Operation</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="95"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="121"/> ++ <source>A file operation is running backend...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="160"/> ++ <source>%1 files, %2</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="260"/> ++ <source>%1 done, %2 total, %3 of %4.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="203"/> ++ <source>clearing: %1, %2 of %3</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="248"/> ++ <source>copying...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="278"/> ++ <source>Syncing...</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FilePreviewPage</name> ++ <message> ++ <source>File Name:</source> ++ <translation type="vanished">文件名称:</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="258"/> ++ <source>File Type:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="286"/> ++ <source>Time Access:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="279"/> ++ <source>Time Modified:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="295"/> ++ <source>Children Count:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="265"/> ++ <source>Size:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="272"/> ++ <source>Time Created:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="304"/> ++ <source>Image resolution:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="311"/> ++ <source>color model:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="380"/> ++ <source>usershare</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Image size:</source> ++ <translation type="vanished">图片尺寸:</translation> ++ </message> ++ <message> ++ <source>Image format:</source> ++ <translation type="vanished">图片格式:</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="435"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="436"/> ++ <source>%1x%2</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="493"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="494"/> ++ <source>%1 total, %2 hidden</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileRenameDialog</name> ++ <message> ++ <source>Names automatically add serial Numbers (e.g., 1,2,3...)</source> ++ <translation type="vanished">名称后自动添加序号(如:1,2,3...)</translation> ++ </message> ++ <message> ++ <source>Cancel</source> ++ <translation type="vanished">取消</translation> ++ </message> ++ <message> ++ <source>New file name</source> ++ <translation type="vanished">文件名</translation> ++ </message> ++ <message> ++ <source>Please enter the file name</source> ++ <translation type="vanished">请输入文件名</translation> ++ </message> ++ <message> ++ <source>OK</source> ++ <translation type="vanished">确定</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileRenameOperation</name> ++ <message> ++ <source>Rename file</source> ++ <translation type="vanished">文件重命名</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="76"/> ++ <source>File Rename error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="77"/> ++ <source>Invalid file name %1%2%3 .</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="93"/> ++ <source>Are you sure to hidden this file?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="126"/> ++ <source>When change the file suffix, the file may be invalid. Are you sure to change it ?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>The file %1%2%3 will be hidden when you refresh or change directory!</source> ++ <translation type="vanished">文件 %1%2%3 在刷新或者切换路径后将会被隐藏!</translation> ++ </message> ++ <message> ++ <source>The file %1%2%3 will be hidden when you refresh or rsort!</source> ++ <translation type="vanished">文件 %1%2%3 在刷新或者排序后将会被隐藏!</translation> ++ </message> ++ <message> ++ <source>The file %1%2%3 will be hidden!</source> ++ <translation type="vanished">文件%1%2%3将被隐藏!</translation> ++ </message> ++ <message> ++ <source>Invalid file name "%1" </source> ++ <translation type="vanished">文件名 "%1" 不合法</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="92"/> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="125"/> ++ <source>File Rename warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>The file "%1" will be hidden!</source> ++ <translation type="vanished">文件 "%1" 将会被隐藏!</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="215"/> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="245"/> ++ <source>Rename file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileTrashOperation</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="72"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="95"/> ++ <source>trash:///</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="75"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="98"/> ++ <source>Trash file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="78"/> ++ <source>Invalid Operation! Can not trash "%1".</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Can not trash</source> ++ <translation type="vanished">不能回收</translation> ++ </message> ++ <message> ++ <source>Can not trash files more than 10GB, would you like to delete it permanently?</source> ++ <translation type="vanished">无法回收大于10G的文件,是否需要永久删除?</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="202"/> ++ <source>An unmanageable conflict exists. Please check the recycle bin.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>The user does not have read and write rights to the file '%1' and cannot delete it to the Recycle Bin.</source> ++ <translation type="vanished">用户对当前文件 %1 没有读写权限,无法删除到回收站。</translation> ++ </message> ++ <message> ++ <source>Can not trash this file, would you like to delete it permanently?</source> ++ <translation type="vanished">不能回收该文件, 是否要永久删除?</translation> ++ </message> ++ <message> ++ <source>Can not trash %1, would you like to delete this file permanently?</source> ++ <translation type="vanished">不能回收%1, 是否永久删除?</translation> ++ </message> ++ <message> ++ <source>. Are you sure you want to permanently delete the file</source> ++ <translation type="vanished">,你确定要永久删除文件吗?</translation> ++ </message> ++ <message> ++ <source>The user does not have read and write rights to the file '%s' and cannot delete it to the Recycle Bin.</source> ++ <translation type="vanished">用户对当前文件 %s 没有读写权限,无法删除到回收站。</translation> ++ </message> ++ <message> ++ <source>Trash file</source> ++ <translation type="vanished">删除文件到回收站</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileUntrashOperation</name> ++ <message> ++ <source>Untrash file</source> ++ <translation type="vanished">撤销删除的文件</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-untrash-operation.cpp" line="159"/> ++ <source>Untrash file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::GlobalSettings</name> ++ <message> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="104"/> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="550"/> ++ <source>yyyy/MM/dd</source> ++ <translation></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="105"/> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="542"/> ++ <source>HH:mm:ss</source> ++ <translation></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="539"/> ++ <source>AP hh:mm:ss</source> ++ <translation></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="553"/> ++ <source>yyyy-MM-dd</source> ++ <translation></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::HeaderBar</name> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="954"/> ++ <source>Spread</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="955"/> ++ <source>Minimize</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="956"/> ++ <source>Close</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::LabelSettings</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="115"/> ++ <source>Name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="116"/> ++ <source>SideBar</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="117"/> ++ <source>Menu</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="124"/> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="178"/> ++ <source>Create New Label</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="126"/> ++ <source>Delete Label</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="136"/> ++ <source>Display the following items in the identification area: (maximum of 6)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="153"/> ++ <source>Rename</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="160"/> ++ <source>Edit Color</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="175"/> ++ <source>Delete This Label</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::LocationBar</name> ++ <message> ++ <source>click the blank area for edit</source> ++ <translation type="vanished">点击空白区域编辑路径</translation> ++ </message> ++ <message> ++ <source>Computer</source> ++ <translation type="obsolete">计算机</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="407"/> ++ <source>Search "%1" in "%2"</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="417"/> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="446"/> ++ <source>File System</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="417"/> ++ <source>Search results for all files marked in "%1" in "%2"</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>&Copy Directory</source> ++ <translation type="vanished">拷贝路径(&C)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="538"/> ++ <source>Open In New Tab</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="542"/> ++ <source>Open In New Window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Open In New &Tab</source> ++ <translation type="vanished">在新标签页中打开(&T)</translation> ++ </message> ++ <message> ++ <source>Open In &New Window</source> ++ <translation type="vanished">在新窗口中打开(&N)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="536"/> ++ <source>Copy Directory</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::MountOperation</name> ++ <message> ++ <location filename="../../libpeony-qt/mount-operation.cpp" line="93"/> ++ <source>Operation Cancelled</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/mount-operation.cpp" line="190"/> ++ <source>Login failed, unknown username or password error, please re-enter!</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::NavigationToolBar</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/navigation-tool-bar.cpp" line="35"/> ++ <source>Go Back</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/navigation-tool-bar.cpp" line="39"/> ++ <source>Go Forward</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/navigation-tool-bar.cpp" line="43"/> ++ <source>History</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/navigation-tool-bar.cpp" line="73"/> ++ <source>Clear History</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/navigation-tool-bar.cpp" line="88"/> ++ <source>Cd Up</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/navigation-tool-bar.cpp" line="94"/> ++ <source>Refresh</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::NewFileLaunchDialog</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="246"/> ++ <source>Choose new application</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="248"/> ++ <source>Choose an Application to open this file</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="255"/> ++ <source>apply now</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="261"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="262"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::OpenWithPropertiesPage</name> ++ <message> ++ <source>How do you want to open %1%2 files ?</source> ++ <translation type="vanished">您希望以什么方式打开 %1%2 文件?</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="111"/> ++ <source>How do you want to open "%1%2" files ?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="116"/> ++ <source>Default open with:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="135"/> ++ <source>Other:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="174"/> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="181"/> ++ <source>Choose other application</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="175"/> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="188"/> ++ <source>Go to application center</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::PathEdit</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/path-bar/path-edit.cpp" line="60"/> ++ <source>Go To</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::PermissionsPropertiesPage</name> ++ <message> ++ <source>User or Group</source> ++ <translation type="vanished">用户或组</translation> ++ </message> ++ <message> ++ <source>Type</source> ++ <translation type="vanished">类型</translation> ++ </message> ++ <message> ++ <source>Readable</source> ++ <translation type="vanished">可读</translation> ++ </message> ++ <message> ++ <source>Writeable</source> ++ <translation type="vanished">可写</translation> ++ </message> ++ <message> ++ <source>Excuteable</source> ++ <translation type="vanished">可执行</translation> ++ </message> ++ <message> ++ <source>File: %1</source> ++ <translation type="vanished">文件:%1</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="84"/> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="168"/> ++ <source>Target: %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="146"/> ++ <source>Read and Write</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="146"/> ++ <source>Readonly</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="146"/> ++ <source>Group or User</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="260"/> ++ <source>(Current User)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="343"/> ++ <source>Current User</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="559"/> ++ <source>Permissions modify tip</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="559"/> ++ <source>The current file or folder has already set ACL permissions. Modifying user group permissions may cause the permissions set in ACL to be unusable. Do you want to continue modifying user group permissions?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="577"/> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="598"/> ++ <source>Permission refinement settings</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="599"/> ++ <source>The current user has set advanced sharing. If you still need to modify permissions, advanced sharing may not be available. Do you want to continue setting?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Read</source> ++ <translation type="vanished">可读</translation> ++ </message> ++ <message> ++ <source>Write</source> ++ <translation type="vanished">可写</translation> ++ </message> ++ <message> ++ <source>Executable</source> ++ <translation type="vanished">可执行</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="190"/> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="199"/> ++ <source>Can not get the permission info.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>(Me)</source> ++ <translation type="vanished">(我)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="331"/> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="333"/> ++ <source>Others</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Owner</source> ++ <translation type="vanished">拥有者</translation> ++ </message> ++ <message> ++ <source>Group</source> ++ <translation type="vanished">用户组</translation> ++ </message> ++ <message> ++ <source>Other</source> ++ <translation type="vanished">其他</translation> ++ </message> ++ <message> ++ <source>Other Users</source> ++ <translation type="vanished">其它用户</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="339"/> ++ <source>You can not change the access of this file.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Me</source> ++ <translation type="vanished">我</translation> ++ </message> ++ <message> ++ <source>User</source> ++ <translation type="vanished">用户</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::PropertiesWindow</name> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="345"/> ++ <source>Trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="349"/> ++ <source>Recent</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="357"/> ++ <source>Selected</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="357"/> ++ <source> %1 Files</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="363"/> ++ <source>usershare</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="374"/> ++ <source>Data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="381"/> ++ <source>Properties</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="481"/> ++ <source>Ok</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="482"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="490"/> ++ <source>Restore</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Close</source> ++ <translation type="vanished">关闭</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::RecentAndTrashPropertiesPage</name> ++ <message> ++ <source>Show confirm dialog while trashing: </source> ++ <translation type="vanished">删除到回收站时弹出确认框:</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp" line="121"/> ++ <source>Show confirm dialog while trashing.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp" line="152"/> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp" line="158"/> ++ <source>Origin Path: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp" line="193"/> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp" line="227"/> ++ <source>Deletion Date: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp" line="179"/> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp" line="242"/> ++ <source>Size: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp" line="236"/> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp" line="243"/> ++ <source>Original Location: </source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SearchBar</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar.cpp" line="47"/> ++ <source>Input the search key of files you would like to find.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar.cpp" line="83"/> ++ <source>Input search key...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar.cpp" line="121"/> ++ <source>advance search</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar.cpp" line="122"/> ++ <source>clear record</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SearchBarContainer</name> ++ <message> ++ <source>Choose File Type</source> ++ <translation type="vanished">选择文件类型</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.cpp" line="259"/> ++ <source>Clear</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="120"/> ++ <source>all</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="120"/> ++ <source>file folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="120"/> ++ <source>image</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="121"/> ++ <source>video</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="121"/> ++ <source>text file</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="121"/> ++ <source>audio</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="121"/> ++ <source>others</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="121"/> ++ <source>wps file</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SharedFileLinkOperation</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/shared-file-link-operation.cpp" line="44"/> ++ <location filename="../../libpeony-qt/file-operation/shared-file-link-operation.cpp" line="47"/> ++ <source>Symbolic Link</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/shared-file-link-operation.cpp" line="80"/> ++ <source>The dest file "%1" has existed!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/shared-file-link-operation.cpp" line="86"/> ++ <source>Link file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SideBarCloudItem</name> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-cloud-item.cpp" line="40"/> ++ <source>CloudStorage</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-cloud-item.cpp" line="55"/> ++ <source>CloudFile</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SideBarFavoriteItem</name> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-favorite-item.cpp" line="84"/> ++ <source>Trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-favorite-item.cpp" line="87"/> ++ <source>Recent</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-favorite-item.cpp" line="95"/> ++ <source>Quick access</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Favorite</source> ++ <translation type="vanished">快速访问</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-favorite-item.cpp" line="189"/> ++ <source>KmreData</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SideBarFileSystemItem</name> ++ <message> ++ <source>Computer</source> ++ <translation type="vanished">计算机</translation> ++ </message> ++ <message> ++ <source>File System</source> ++ <translation type="vanished">文件系统</translation> ++ </message> ++ <message> ++ <source>data</source> ++ <translation type="vanished">数据盘</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-file-system-item.cpp" line="168"/> ++ <source>Data</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SideBarMenu</name> ++ <message> ++ <source>&Properties</source> ++ <translation type="vanished">属性(&P)</translation> ++ </message> ++ <message> ++ <source>P&roperties</source> ++ <translation type="vanished">属性(&R)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="68"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="91"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="117"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="136"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="317"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="371"/> ++ <source>Properties</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="102"/> ++ <source>Delete Symbolic</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="188"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="365"/> ++ <source>Unmount</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="197"/> ++ <source>Eject</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="225"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="258"/> ++ <source>Format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="294"/> ++ <source>burndata</source> ++ <translation></translation> ++ </message> ++ <message> ++ <source>&Delete Symbolic</source> ++ <translation type="vanished">删除(&D)</translation> ++ </message> ++ <message> ++ <source>&Unmount</source> ++ <translation type="vanished">卸载(&U)</translation> ++ </message> ++ <message> ++ <source>&Eject</source> ++ <translation type="vanished">弹出(&E)</translation> ++ </message> ++ <message> ++ <source>format</source> ++ <translation type="vanished">格式化</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SideBarModel</name> ++ <message> ++ <source>Shared Data</source> ++ <translation type="vanished">共享数据</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-model.cpp" line="98"/> ++ <source>Network</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SideBarPersonalItem</name> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-personal-item.cpp" line="42"/> ++ <source>Personal</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SideBarSeparatorItem</name> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-separator-item.h" line="68"/> ++ <source>(No Sub Directory)</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::StatusBar</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/status-bar/status-bar.cpp" line="94"/> ++ <location filename="../../libpeony-qt/controls/status-bar/status-bar.cpp" line="100"/> ++ <source>; %1 folders</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/status-bar/status-bar.cpp" line="95"/> ++ <location filename="../../libpeony-qt/controls/status-bar/status-bar.cpp" line="102"/> ++ <source>; %1 files, %2 total</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/status-bar/status-bar.cpp" line="97"/> ++ <source>; %1 folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/status-bar/status-bar.cpp" line="98"/> ++ <source>; %1 file, %2</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/status-bar/status-bar.cpp" line="105"/> ++ <source>%1 selected</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SyncThread</name> ++ <message> ++ <location filename="../../libpeony-qt/sync-thread.cpp" line="66"/> ++ <source>notify</source> ++ <translatorcomment>温馨提示</translatorcomment> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::TagManagement</name> ++ <message> ++ <source>General</source> ++ <translation type="vanished">通用</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="313"/> ++ <source>Mark</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Sidebar</source> ++ <translation type="vanished">边栏</translation> ++ </message> ++ <message> ++ <source>Advanced</source> ++ <translation type="vanished">高级</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::ToolBar</name> ++ <message> ++ <source>Open in new &Window</source> ++ <translation type="vanished">在新窗口中打开(&W)</translation> ++ </message> ++ <message> ++ <source>Open in &New window</source> ++ <translation type="vanished">在新窗口中打开(&N)</translation> ++ </message> ++ <message> ++ <source>Open in new &Tab</source> ++ <translation type="vanished">在新标签页中打开(&T)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="138"/> ++ <source>Sort Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="140"/> ++ <source>File Name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="146"/> ++ <source>File Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="149"/> ++ <source>File Size</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="143"/> ++ <source>Modified Date</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="72"/> ++ <source>Open in New window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="74"/> ++ <source>Open in new Tab</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="160"/> ++ <source>Ascending</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="156"/> ++ <source>Descending</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="190"/> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="340"/> ++ <source>Copy</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="193"/> ++ <source>Paste</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="196"/> ++ <source>Cut</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="199"/> ++ <source>Trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="216"/> ++ <source>Clean Trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Delete file Warning</source> ++ <translation type="vanished">删除文件警告</translation> ++ </message> ++ <message> ++ <source>Delete Permanently</source> ++ <translation type="vanished">永久删除</translation> ++ </message> ++ <message> ++ <source>Are you sure that you want to delete these files? Once you start a deletion, the files deleting will never be restored again.</source> ++ <translation type="vanished">您确定要删除这些文件吗?一旦开始删除,这些文件将不可再恢复。</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="221"/> ++ <source>Restore</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="270"/> ++ <source>Options</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="273"/> ++ <source>Forbid Thumbnail</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="281"/> ++ <source>Show Hidden</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="288"/> ++ <source>Resident in Backend</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="289"/> ++ <source>Let the program still run after closing the last window. This will reduce the time for the next launch, but it will also consume resources in backend.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="301"/> ++ <source>&Help</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="307"/> ++ <source>&About...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="309"/> ++ <source>Peony Qt</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="310"/> ++ <source>Author: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Author: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> ++ <translation type="vanished">作者: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++版权所有(C): 2019,天津麒麟信息技术有限公司.</translation> ++ </message> ++ <message> ++ <source>Authour: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> ++ <translation type="vanished">作者: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++版权所有(C): 2019,天津麒麟信息技术有限公司.</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::UserShareInfoManager</name> ++ <message> ++ <location filename="../../libpeony-qt/usershare-manager.cpp" line="115"/> ++ <location filename="../../libpeony-qt/usershare-manager.cpp" line="148"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::VolumeManager</name> ++ <message> ++ <location filename="../../libpeony-qt/volume-manager.cpp" line="160"/> ++ <source>Error</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>ProgressBar</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="860"/> ++ <source>starting ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="957"/> ++ <source>canceling ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="959"/> ++ <source>sync ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="1014"/> ++ <source>continue</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="1016"/> ++ <source>pause</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="1023"/> ++ <source>close</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>cancel file operation</source> ++ <translation type="vanished">取消文件操作</translation> ++ </message> ++ <message> ++ <source>Are you sure want to cancel the current selected file operation</source> ++ <translation type="vanished">你确定要取消当前选中的文件操作</translation> ++ </message> ++ <message> ++ <source>OK</source> ++ <translation type="vanished">确定</translation> ++ </message> ++ <message> ++ <source>Cancel</source> ++ <translation type="vanished">取消</translation> ++ </message> ++</context> ++<context> ++ <name>QObject</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/icon-view-factory.h" line="40"/> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/icon-view-factory.h" line="60"/> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/icon-view-factory.h" line="93"/> ++ <source>Icon View</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/icon-view-factory.h" line="46"/> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/icon-view-factory.h" line="99"/> ++ <source>Show the folder children as icons.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/list-view-factory.h" line="42"/> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/list-view-factory.h" line="62"/> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/list-view-factory.h" line="95"/> ++ <source>List View</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/list-view-factory.h" line="48"/> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/list-view-factory.h" line="101"/> ++ <source>Show the folder children as rows in a list.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Basic Preview Page</source> ++ <translation type="vanished">基本</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page-factory.h" line="40"/> ++ <source>Basic</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page-factory.h" line="46"/> ++ <source>Show the basic file properties, and allow you to modify the access and name.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Permissions Page</source> ++ <translation type="vanished">权限</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page-factory.h" line="41"/> ++ <source>Permissions</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page-factory.h" line="47"/> ++ <source>Show and modify file's permission, owner and group.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Can not trash</source> ++ <translation type="vanished">不能回收</translation> ++ </message> ++ <message> ++ <source>Can not trash these files. You can delete them permanently. Are you sure doing that?</source> ++ <translation type="vanished">这些文件不能完全放入回收站,可以选择永久删除这些文件,确定这样做吗?</translation> ++ </message> ++ <message> ++ <source>Can not trash files more than 10GB, would you like to delete it permanently?</source> ++ <translation type="vanished">无法回收大于10G的文件,是否需要永久删除?</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="195"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="108"/> ++ <source>The file is too large to be moved to the recycle bin. Do you want to permanently delete it?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="199"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="111"/> ++ <source>These files are too large to be moved to the recycle bin. Do you want to permanently delete these %1 files?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="311"/> ++ <source>Clean the Trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>OK</source> ++ <translation type="vanished">清空回收站</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="323"/> ++ <source>Do you want to empty the recycle bin and delete the files permanently? Once it has begun there is no way to restore them.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Delete Permanently</source> ++ <translation type="vanished">永久删除</translation> ++ </message> ++ <message> ++ <source>Are you sure that you want to delete these files? Once you start a deletion, the files deleting will never be restored again.</source> ++ <translation type="vanished">您确定要删除这些文件吗?一旦开始删除,这些文件将不可再恢复。</translation> ++ </message> ++ <message> ++ <source>Computer Properties Page</source> ++ <translation type="vanished">计算机</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page-factory.h" line="41"/> ++ <source>Computer Properties</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page-factory.h" line="47"/> ++ <source>Show the computer properties or items in computer.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Trash and Recent Properties Page</source> ++ <translation type="vanished">最近/回收</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page-factory.h" line="40"/> ++ <source>Trash and Recent</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page-factory.h" line="46"/> ++ <source>Show the file properties or items in trash or recent.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>eject device failed</source> ++ <translation type="vanished">弹出设备失败</translation> ++ </message> ++ <message> ++ <source>Please check whether the device is occupied and then eject the device again</source> ++ <translation type="vanished">请检查设备是否正在使用,确认没有使用后再次弹出</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="544"/> ++ <source>Format failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="546"/> ++ <source>YES</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1033"/> ++ <source>Formatting successful! But failed to set the device name.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1050"/> ++ <source>qmesg_notify</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1065"/> ++ <source>Format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1067"/> ++ <source>Begin Format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1070"/> ++ <source>Close</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1031"/> ++ <source>Format operation has been finished successfully.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Formatting successful! Description Failed to set the device name.</source> ++ <translation type="vanished">格式化成功!设备名设置失败。</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1050"/> ++ <source>Sorry, the format operation is failed!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1063"/> ++ <source>Formatting this volume will erase all data on it. Please backup all retained data before formatting. Do you want to continue ?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1031"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1033"/> ++ <source>format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>begin format</source> ++ <translation type="vanished">开始</translation> ++ </message> ++ <message> ++ <source>close</source> ++ <translation type="vanished">关闭</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/sync-thread.cpp" line="63"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="950"/> ++ <source>File Manager</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Default search vfs of peony</source> ++ <translation type="vanished">默认文件搜索</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="177"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1754"/> ++ <source>Force unmount failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-net-work-item.cpp" line="136"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="177"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1754"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="542"/> ++ <source>Error: %1 ++</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-net-work-item.cpp" line="142"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1757"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1807"/> ++ <source>Data synchronization is complete,the device has been unmount successfully!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-net-work-item.cpp" line="131"/> ++ <location filename="../../libpeony-qt/model/side-bar-net-work-item.cpp" line="136"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1786"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1789"/> ++ <source>Unmount failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1774"/> ++ <source>Not authorized to perform operation.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-net-work-item.cpp" line="131"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1786"/> ++ <source>Unable to unmount it, you may need to close some programs, such as: GParted etc.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1789"/> ++ <source>Error: %1 ++Do you want to unmount forcely?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="316"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Eject Anyway</source> ++ <translation type="vanished">无论如何弹出</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1250"/> ++ <source>Failed to activate device: Incorrect passphrase</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1260"/> ++ <source>The device has been mount successfully!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1453"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1491"/> ++ <source>Eject device failed, the reason may be that the device has been removed, etc.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1459"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1498"/> ++ <source>Data synchronization is complete and the device can be safely unplugged!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Unable to eject %1</source> ++ <translation type="vanished">无法弹出 %1</translation> ++ </message> ++ <message> ++ <source>PeonyNotify</source> ++ <translation type="vanished">文件管理器通知</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1455"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1493"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1774"/> ++ <source>Eject failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="266"/> ++ <source>favorite</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="269"/> ++ <source>Favorites</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="304"/> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="309"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="382"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="387"/> ++ <source>File is not existed.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="317"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="395"/> ++ <source>Share Data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="321"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="399"/> ++ <source>Trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="325"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="403"/> ++ <source>Recent</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-file.cpp" line="337"/> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-file.cpp" line="428"/> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-file.cpp" line="462"/> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="371"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="250"/> ++ <source>Operation not supported</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="466"/> ++ <source>The virtual file system does not support folder creation</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="538"/> ++ <source>Can not create a symbolic file for vfs location</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="545"/> ++ <source>Symbolic Link</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="557"/> ++ <source>Can not create symbolic file here, %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="566"/> ++ <source>Can not add a file to favorite directory.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="624"/> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="632"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="231"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="267"/> ++ <source>The virtual file system cannot be opened</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-file.cpp" line="378"/> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-file.cpp" line="520"/> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-file.cpp" line="563"/> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-file.cpp" line="577"/> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="453"/> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="481"/> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="496"/> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="582"/> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="600"/> ++ <source>Virtual file directories do not support move and copy operations</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-register.h" line="43"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-register.h" line="42"/> ++ <source>Default favorite vfs of peony</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page-factory.h" line="38"/> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page-factory.h" line="44"/> ++ <source>Details</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/mark-properties-page-factory.h" line="40"/> ++ <source>Mark</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/mark-properties-page-factory.h" line="46"/> ++ <source>mark this file.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page-factory.h" line="40"/> ++ <source>Open With</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page-factory.h" line="46"/> ++ <source>open with.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/sync-thread.cpp" line="33"/> ++ <source>It need to synchronize before operating the device,place wait!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-enumerator.cpp" line="412"/> ++ <location filename="../../libpeony-qt/file-enumerator.cpp" line="425"/> ++ <source>Unable to discover the file, it may have been removed or deleted.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-enumerator.cpp" line="419"/> ++ <source>permission denied</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>file not found</source> ++ <translation type="vanished">没有发现该文件</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="536"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="553"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="569"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="862"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="162"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="184"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="206"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="213"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="229"/> ++ <source>duplicate</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Error when getting information for file : No target file found</source> ++ <translation type="vanished">获取文件信息时出现错误:没有目标文件。</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="353"/> ++ <source>data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="371"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="345"/> ++ <source>label</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-file-system-item.cpp" line="185"/> ++ <location filename="../../libpeony-qt/vfs/search-vfs-uri-parser.cpp" line="110"/> ++ <source>Computer</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="356"/> ++ <location filename="../../libpeony-qt/model/side-bar-file-system-item.cpp" line="257"/> ++ <source>File System</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-file-system-item.cpp" line="262"/> ++ <source>Data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="411"/> ++ <source>Failed to open file "%1": insufficient permissions.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="422"/> ++ <source>File “%1” does not exist. Please check whether the file has been deleted.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/disc/disccommand.cpp" line="81"/> ++ <source>burn operation has been cancelled</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/disc/disccommand.cpp" line="85"/> ++ <source> is busy!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1298"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="203"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="378"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="182"/> ++ <source>Are you sure you want to permanently delete this file? Once deletion begins, the file will not be recoverable.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1302"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="207"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="382"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="186"/> ++ <source>Are you sure you want to permanently delete these %1 files? Once deletion begins, these file will not be recoverable.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="283"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="416"/> ++ <source>Virtual file directories do not support move operations</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-register.h" line="41"/> ++ <source>test simplify vfs plugin</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-register.h" line="73"/> ++ <source>Default custom vfs info of peony</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-register.h" line="115"/> ++ <source>Default local vfs info of peony</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window-factory.h" line="61"/> ++ <source>Show properties plugin window.</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>UdfBurn::UdfAppendBurnDataDialog</name> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="45"/> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="180"/> ++ <source>AppendBurnData</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="58"/> ++ <source>Disc Type:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="66"/> ++ <source>Device Name:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="79"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="81"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="89"/> ++ <source>Unknown</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="131"/> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="148"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="131"/> ++ <source>No burn data, please add!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="148"/> ++ <source>The disc name cannot be set to empty, please re-enter it!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="180"/> ++ <source>AppendBurnData operation has been finished successfully.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="186"/> ++ <source>Sorry, the appendBurnData operation is failed!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="187"/> ++ <source>Failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="196"/> ++ <source>Burning. Do not close this window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="205"/> ++ <source>Burning this disc will append datas on it. Do you want to continue ?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="206"/> ++ <source>Burn</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="208"/> ++ <source>Begin Burning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="209"/> ++ <source>Close</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>UdfBurn::UdfFormatDialog</name> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="47"/> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="166"/> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="217"/> ++ <source>Format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="60"/> ++ <source>Disc Type:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="68"/> ++ <source>Device Name:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="82"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="84"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="92"/> ++ <source>Unknown</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="131"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="131"/> ++ <source>The disc name cannot be set to empty, please re-enter it!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="166"/> ++ <source>Format operation has been finished successfully.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="171"/> ++ <source>Sorry, the format operation is failed!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="172"/> ++ <source>Failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="206"/> ++ <source>Formatting. Do not close this window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="216"/> ++ <source>Formatting this disc will erase all data on it. Please backup all retained data before formatting. Do you want to continue ?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="219"/> ++ <source>Begin Format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="220"/> ++ <source>Close</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>UdfFormatDialog</name> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="41"/> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="165"/> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="218"/> ++ <source>Format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="54"/> ++ <source>Disc Type:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="62"/> ++ <source>Device Name:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="76"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="78"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="86"/> ++ <source>Unknown</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="128"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="128"/> ++ <source>The disc name cannot be set to empty, please re-enter it!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="165"/> ++ <source>Format operation has been finished successfully.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="170"/> ++ <source>Sorry, the format operation is failed!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="171"/> ++ <source>Failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="205"/> ++ <source>Formatting. Do not close this window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="217"/> ++ <source>Formatting this disc will erase all data on it. Please backup all retained data before formatting. Do you want to continue ?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="220"/> ++ <source>Begin Format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="221"/> ++ <source>Close</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++</TS> +diff --git a/translations/peony-qt/peony-qt_ar.ts b/translations/peony-qt/peony-qt_ar.ts +new file mode 100644 +index 0000000..d9289b2 +--- /dev/null ++++ b/translations/peony-qt/peony-qt_ar.ts +@@ -0,0 +1,1387 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<!DOCTYPE TS> ++<TS version="2.1" language="ar"> ++<context> ++ <name>AboutDialog</name> ++ <message> ++ <location filename="../../src/windows/about-dialog.ui" line="32"/> ++ <source>Dialog</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/about-dialog.ui" line="88"/> ++ <source><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> ++<html><head><meta name="qrichtext" content="1" /><style type="text/css"> ++p, li { white-space: pre-wrap; } ++</style></head><body style=" font-family:'Noto Sans CJK SC'; font-size:10pt; font-weight:400; font-style:normal;"> ++<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><br /></p></body></html></source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/about-dialog.ui" line="115"/> ++ <source>TextLabel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Offical Website: </source> ++ <translation type="vanished">官方网站: </translation> ++ </message> ++ <message> ++ <source>Service & Technology Support: </source> ++ <translation type="vanished">服务与技术支持: </translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/about-dialog.cpp" line="47"/> ++ <location filename="../../src/windows/about-dialog.cpp" line="168"/> ++ <source>Service & Support: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/about-dialog.cpp" line="46"/> ++ <location filename="../../src/windows/about-dialog.cpp" line="98"/> ++ <location filename="../../src/windows/about-dialog.cpp" line="114"/> ++ <source>Peony</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>peony</source> ++ <translation type="vanished">文件管理器</translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/about-dialog.cpp" line="51"/> ++ <location filename="../../src/windows/about-dialog.cpp" line="129"/> ++ <source>Peony is a graphical software to help users manage system files. It provides common file operation functions for users, such as file viewing, file copy, paste, cut, delete, rename, file selection, application opening, file search, file sorting, file preview, etc. it is convenient for users to manage system files intuitively on the interface.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Hot Service: </source> ++ <translation type="vanished">服务热线: </translation> ++ </message> ++ <message> ++ <source>File Manager</source> ++ <translation type="vanished">文件管理器</translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/about-dialog.cpp" line="50"/> ++ <location filename="../../src/windows/about-dialog.cpp" line="117"/> ++ <source>Version number: %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>File manager is a graphical software to help users manage system files. It provides common file operation functions for users, such as file viewing, file copy, paste, cut, delete, rename, file selection, application opening, file search, file sorting, file preview, etc. it is convenient for users to manage system files intuitively on the interface.</source> ++ <translation type="vanished">文件管理器是一款帮助用户管理系统文件的图形化的软件,为用户提供常用的文件操作功能,比如文件查看,文件复制、粘贴、剪切、删除、重命名,文件打开方式选择,文件搜索,文件排序,文件预览等,方便用户在界面上直观地管理系统文件。</translation> ++ </message> ++ <message> ++ <source>none</source> ++ <translation type="vanished">无</translation> ++ </message> ++</context> ++<context> ++ <name>FileLabelBox</name> ++ <message> ++ <location filename="../../src/control/file-label-box.cpp" line="72"/> ++ <source>Rename</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/file-label-box.cpp" line="77"/> ++ <source>Edit Color</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Delete</source> ++ <translation type="vanished">删除标记</translation> ++ </message> ++ <message> ++ <source>Create New Label</source> ++ <translation type="vanished">创建标记</translation> ++ </message> ++</context> ++<context> ++ <name>HeaderBar</name> ++ <message> ++ <source>Create Folder</source> ++ <translation type="vanished">新建文件夹</translation> ++ </message> ++ <message> ++ <source>Open Terminal</source> ++ <translation type="vanished">打开终端</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="136"/> ++ <source>Go Back</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="144"/> ++ <source>Go Forward</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Search</source> ++ <translation type="vanished">搜索</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="236"/> ++ <source>View Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="274"/> ++ <source>Sort Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="356"/> ++ <source>Option</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="472"/> ++ <source>Operate Tips</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="163"/> ++ <source>Go Up</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Details</source> ++ <translation type="vanished">详情</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="369"/> ++ <source>&Copy</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="372"/> ++ <source>Copy</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="384"/> ++ <source>&Cut</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="387"/> ++ <source>Cut</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="393"/> ++ <source>&Select All</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="397"/> ++ <location filename="../../src/control/header-bar.cpp" line="409"/> ++ <source>Select All</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="414"/> ++ <location filename="../../src/control/header-bar.cpp" line="926"/> ++ <location filename="../../src/control/header-bar.cpp" line="944"/> ++ <source>Deselect All</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="419"/> ++ <source>&Delete to trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="422"/> ++ <source>Delete to trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="473"/> ++ <source>Don't find any terminal, please install at least one terminal!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="728"/> ++ <location filename="../../src/control/header-bar.cpp" line="929"/> ++ <location filename="../../src/control/header-bar.cpp" line="939"/> ++ <source>Select All Item</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="738"/> ++ <location filename="../../src/control/header-bar.cpp" line="873"/> ++ <location filename="../../src/control/header-bar.cpp" line="912"/> ++ <source>Select</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="713"/> ++ <source>Restore</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="716"/> ++ <source>Maximize</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="915"/> ++ <source>Select Done</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="765"/> ++ <source>MoveTo</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="207"/> ++ <source>Icon View</source> ++ <translatorcomment>图标视图</translatorcomment> ++ <translation></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="217"/> ++ <source>List View</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="774"/> ++ <source>CopyTo</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="783"/> ++ <source>Delete</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="850"/> ++ <source>Select path</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Minimize</source> ++ <translation type="vanished">最小化</translation> ++ </message> ++ <message> ++ <source>Close</source> ++ <translation type="vanished">关闭</translation> ++ </message> ++</context> ++<context> ++ <name>HeaderBarContainer</name> ++ <message> ++ <source>Option</source> ++ <translation type="vanished">选项</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="1295"/> ++ <source>Minimize</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Maximize/Restore</source> ++ <translation type="vanished">最大化/还原</translation> ++ </message> ++ <message> ++ <source>Restore</source> ++ <translation type="vanished">还原</translation> ++ </message> ++ <message> ++ <source>Maximize</source> ++ <translation type="vanished">最大化</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="1307"/> ++ <source>Close</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Intel::NavigationSideBar</name> ++ <message> ++ <location filename="../../src/control/intel/intel-navigation-side-bar.cpp" line="180"/> ++ <source>Open In &New Window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-navigation-side-bar.cpp" line="204"/> ++ <source>Open In New &Tab</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Intel::TitleLabel</name> ++ <message> ++ <location filename="../../src/control/intel/intel-navigation-side-bar.cpp" line="516"/> ++ <source>Files</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>MainWindow</name> ++ <message> ++ <location filename="../../src/windows/main-window.cpp" line="889"/> ++ <source>File Manager</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/main-window.cpp" line="411"/> ++ <source>Undo</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/main-window.cpp" line="418"/> ++ <source>Redo</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/main-window.cpp" line="790"/> ++ <source>warn</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/main-window.cpp" line="790"/> ++ <source>This operation is not supported.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/main-window.cpp" line="887"/> ++ <source>Search</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Tips info</source> ++ <translation type="vanished">温馨提示</translation> ++ </message> ++ <message> ++ <source>Trash has no file need to be cleaned.</source> ++ <translation type="vanished">回收站没有文件需要被清空!</translation> ++ </message> ++ <message> ++ <source>Delete file Warning</source> ++ <translation type="vanished">删除文件警告</translation> ++ </message> ++ <message> ++ <source>Delete Permanently</source> ++ <translation type="vanished">永久删除</translation> ++ </message> ++ <message> ++ <source>Are you sure that you want to delete these files? Once you start a deletion, the files deleting will never be restored again.</source> ++ <translation type="vanished">您确定要删除这些文件吗?一旦开始删除,这些文件将不可再恢复。</translation> ++ </message> ++ <message> ++ <source>Peony Qt</source> ++ <translation type="vanished">文件管理器</translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/main-window.cpp" line="902"/> ++ <source>New Folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>NavigationSideBar</name> ++ <message> ++ <source>All tags...</source> ++ <translation type="vanished">所有标记...</translation> ++ </message> ++ <message> ++ <source>Open In &New Window</source> ++ <translation type="vanished">在新窗口中打开(&N)</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="590"/> ++ <source>warn</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="590"/> ++ <source>This operation is not supported.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="224"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="628"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="646"/> ++ <source>Tips</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="224"/> ++ <source>The device is in busy state, please perform this operation later.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="628"/> ++ <source>This is an abnormal Udisk, please fix it or format it</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="646"/> ++ <source>This is an empty drive, please insert a Disc.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="259"/> ++ <source>Open In New Window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="275"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="309"/> ++ <source>Can not open %1, %2</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="293"/> ++ <source>Open In New Tab</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Open In New &Tab</source> ++ <translation type="vanished">在新标签页中打开(&T)</translation> ++ </message> ++</context> ++<context> ++ <name>NavigationSideBarContainer</name> ++ <message> ++ <source>All tags...</source> ++ <translation type="vanished">所有标记...</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="848"/> ++ <source>Manager tags...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="851"/> ++ <source>More tags...</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>NavigationTabBar</name> ++ <message> ++ <source>Computer</source> ++ <translation type="vanished">计算机</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-tab-bar.cpp" line="131"/> ++ <source>Search "%1" in "%2"</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>OperationMenu</name> ++ <message> ++ <source>Advance Search</source> ++ <translation type="vanished">高级搜索</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="78"/> ++ <source>Keep Allow</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="90"/> ++ <source>Show Hidden</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="98"/> ++ <source>Show File Extension</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="103"/> ++ <source>Show Create Time</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="110"/> ++ <source>Show Relative Time</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="123"/> ++ <source>Forbid thumbnailing</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="133"/> ++ <source>Resident in Backend</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="142"/> ++ <source>Parallel Operations</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="148"/> ++ <source>Set samba password</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="162"/> ++ <source>Tips</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="162"/> ++ <source>The user already has a samba password, do you need to reset the samba password?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="173"/> ++ <source>Samba set user password</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="171"/> ++ <source>Samba password:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="181"/> ++ <location filename="../../src/control/operation-menu.cpp" line="192"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="181"/> ++ <source>Samba set password failed, Please re-enter!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="192"/> ++ <source>Shared configuration service exception, please confirm if there is an ongoing shared configuration operation, or please reset the share!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="201"/> ++ <source>Open each folder in a new window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="207"/> ++ <source>Plugin manager Settings</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="216"/> ++ <source>Help</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="220"/> ++ <source>About</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>OperationMenuEditWidget</name> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="284"/> ++ <source>Edit</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="295"/> ++ <source>copy</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="304"/> ++ <source>paste</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="313"/> ++ <source>cut</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="322"/> ++ <source>trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::Intel::SideBarCloudItem</name> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-cloud-item.cpp" line="40"/> ++ <source>CloudStorage</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-cloud-item.cpp" line="55"/> ++ <source>CloudFile</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::Intel::SideBarFavoriteItem</name> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-favorite-item.cpp" line="46"/> ++ <source>Favorites</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::Intel::SideBarFileSystemItem</name> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="60"/> ++ <source>Computer</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="79"/> ++ <source>文件系统</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="80"/> ++ <source>System Disk</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::Intel::SideBarMenu</name> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="52"/> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="71"/> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="97"/> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="112"/> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="174"/> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="242"/> ++ <source>Properties</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="82"/> ++ <source>Delete Symbolic</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="132"/> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="234"/> ++ <source>Unmount</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="151"/> ++ <source>Eject</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="203"/> ++ <source>format</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::Intel::SideBarPersonalItem</name> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-personal-item.cpp" line="45"/> ++ <source>Personal</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::Intel::SideBarSeparatorItem</name> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-separator-item.h" line="70"/> ++ <source>(No Sub Directory)</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::Intel::SideBarUserDiskItem</name> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="722"/> ++ <source>User Disk</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::Intel::TabletSideBarFactory</name> ++ <message> ++ <location filename="../../src/control/intel/tablet-side-bar-factory.cpp" line="60"/> ++ <source>Intel Side Bar</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SearchWidget</name> ++ <message> ++ <location filename="../../src/control/search-widget.cpp" line="50"/> ++ <source>Search</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::TrashWarnDialog</name> ++ <message> ++ <location filename="../../src/windows/trash-warn-dialog.cpp" line="101"/> ++ <source>Are you sure that you want to delete these files? Once you start a deletion, the files deleting will never be restored again.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/trash-warn-dialog.cpp" line="114"/> ++ <source>Delete</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/trash-warn-dialog.cpp" line="115"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>PeonyApplication</name> ++ <message> ++ <source>Peony-Qt</source> ++ <translation type="vanished">文件管理器</translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.cpp" line="170"/> ++ <source>peony-qt</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.cpp" line="177"/> ++ <source>Files or directories to open</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.cpp" line="177"/> ++ <source>[FILE1, FILE2,...]</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.cpp" line="224"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.cpp" line="224"/> ++ <source>Peony-Qt can not get the system's icon theme. There are 2 reasons might lead to this problem: ++ ++1. Peony-Qt might be running as root, that means you have the higher permission and can do some things which normally forbidden. But, you should learn that if you were in a root, the virtual file system will lose some featrue such as you can not use "My Computer", the theme and icons might also went wrong. So, run peony-qt in a root is not recommended. ++ ++2. You are using a non-qt theme for your system but you didn't install the platform theme plugin for qt's applications. If you are using gtk-theme, try installing the qt5-gtk2-platformtheme package to resolve this problem.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.cpp" line="495"/> ++ <source>Peony Qt</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.cpp" line="496"/> ++ <source>Author: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2020, KylinSoft Co., Ltd.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Author: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019-2020, KYLIN Software Co., Ltd.</source> ++ <translation type="vanished">作者: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++版权所有(C): 2019-2020,麒麟软件有限公司.</translation> ++ </message> ++ <message> ++ <source>Author: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019-2020, KYLIN Software Co., Ltd.</source> ++ <translation type="vanished">作者: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++版权所有(C): 2019-2020,麒麟软件有限公司.</translation> ++ </message> ++ <message> ++ <source>Authour: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019-2020, KYLIN Software Co., Ltd.</source> ++ <translation type="vanished">作者: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++版权所有(C): 2019-2020,麒麟软件有限公司.</translation> ++ </message> ++ <message> ++ <source>Authour: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019-2020, Tianjin KYLIN Information Technology Co., Ltd.</source> ++ <translation type="vanished">作者: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++版权所有(C): 2019-2020,天津麒麟信息技术有限公司.</translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.h" line="67"/> ++ <source>Close all peony-qt windows and quit</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.h" line="68"/> ++ <source>Show items</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.h" line="69"/> ++ <source>Show folders</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.h" line="70"/> ++ <source>Show properties</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>QObject</name> ++ <message> ++ <source>Error</source> ++ <translation type="vanished">错误</translation> ++ </message> ++ <message> ++ <source>Can not open %1.</source> ++ <translation type="vanished">无法打开 %1.</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="451"/> ++ <source>Force unmount failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="451"/> ++ <source>Error: %1 ++</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="455"/> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="494"/> ++ <source>Data synchronization is complete,the device has been unmount successfully!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="473"/> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="478"/> ++ <source>Unmount failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="473"/> ++ <source>Unable to unmount it, you may need to close some programs, such as: GParted etc.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="478"/> ++ <source>Error: %1 ++Do you want to unmount forcely?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="630"/> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="684"/> ++ <source>Eject failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="631"/> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="685"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="632"/> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="686"/> ++ <source>Eject Anyway</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="644"/> ++ <source>Data synchronization is complete and the device can be safely unplugged!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="682"/> ++ <source>Unable to eject %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="700"/> ++ <source>PeonyNotify</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="701"/> ++ <source>File Manager</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>SortTypeMenu</name> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="34"/> ++ <source>File Name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="46"/> ++ <source>File Size</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="50"/> ++ <source>Original Path</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="82"/> ++ <source>Use current sorting for all folders</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="102"/> ++ <source>By %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="111"/> ++ <source>Newest to oldest</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="112"/> ++ <source>Oldest to newest</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="114"/> ++ <source>Files from large to small</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="115"/> ++ <source>Files from small to large</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Use global sorting</source> ++ <translation type="vanished">使用全局排序</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="42"/> ++ <source>File Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="38"/> ++ <source>Modified Date</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Modified Data</source> ++ <translation type="vanished">修改日期</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="71"/> ++ <location filename="../../src/control/sort-type-menu.cpp" line="118"/> ++ <source>Ascending</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="66"/> ++ <location filename="../../src/control/sort-type-menu.cpp" line="117"/> ++ <source>Descending</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>TabStatusBar</name> ++ <message> ++ <source>Current path has:</source> ++ <translation type="vanished">当前路径包含:</translation> ++ </message> ++ <message> ++ <source>%1 folders, %2 files</source> ++ <translation type="vanished">%1 文件夹,%2 文件</translation> ++ </message> ++ <message> ++ <source>%1 folders</source> ++ <translation type="vanished">%1 文件夹</translation> ++ </message> ++ <message> ++ <source>%1 files</source> ++ <translation type="vanished">%1 文件</translation> ++ </message> ++ <message> ++ <source>; %1 folders</source> ++ <translation type="vanished">; %1 个文件夹</translation> ++ </message> ++ <message> ++ <source>; %1 files, %2 total</source> ++ <translation type="vanished">; %1 个文件, 共%2</translation> ++ </message> ++ <message> ++ <source>; %1 folder</source> ++ <translation type="vanished">; %1 个文件夹</translation> ++ </message> ++ <message> ++ <source>; %1 file, %2</source> ++ <translation type="vanished">; %1 个文件, %2</translation> ++ </message> ++ <message> ++ <source>%1 selected</source> ++ <translation type="vanished">选中%1个</translation> ++ </message> ++ <message> ++ <source>Search "%1" in "%2"</source> ++ <translation type="vanished">在%2中搜索%1</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-status-bar.cpp" line="122"/> ++ <source> %1 items </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-status-bar.cpp" line="109"/> ++ <location filename="../../src/control/tab-status-bar.cpp" line="218"/> ++ <source>Searching for files ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>selected%1%2</source> ++ <translation type="vanished">选中%1%2</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-status-bar.cpp" line="159"/> ++ <source> selected %1 items %2</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source> %1 items selected %2 items</source> ++ <translation type="vanished">%1 项 选中 %2 项</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-status-bar.cpp" line="161"/> ++ <source> selected %1 items</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>TabWidget</name> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="285"/> ++ <source>Trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="289"/> ++ <source>Clear</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="294"/> ++ <source>Recover</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="451"/> ++ <source>Computer</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Close Filter.</source> ++ <translation type="vanished">关闭筛选。</translation> ++ </message> ++ <message> ++ <source>Filter</source> ++ <translation type="vanished">筛选</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="526"/> ++ <source>Select Path</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="1347"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="1349"/> ++ <source>Error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="1708"/> ++ <source>Opening such files is not currently supported</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="1722"/> ++ <source>Open failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="1723"/> ++ <source>Open directory failed, you have no permission!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Close advance search.</source> ++ <translation type="vanished">关闭高级搜索。</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="441"/> ++ <source>Search</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Choose other path to search.</source> ++ <translation type="vanished">选择其他搜索路径。</translation> ++ </message> ++ <message> ++ <source>Search recursively</source> ++ <translation type="vanished">递归搜索</translation> ++ </message> ++ <message> ++ <source>more options</source> ++ <translation type="vanished">更多选项</translation> ++ </message> ++ <message> ++ <source>Show/hide advance search</source> ++ <translation type="vanished">显示/隐藏高级搜索</translation> ++ </message> ++ <message> ++ <source>Select path</source> ++ <translation type="vanished">选择路径</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="586"/> ++ <location filename="../../src/control/tab-widget.cpp" line="746"/> ++ <source>is</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="621"/> ++ <source>Please input key words...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Please input kay words...</source> ++ <translation type="vanished">请输入关键词...</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="707"/> ++ <location filename="../../src/control/tab-widget.cpp" line="730"/> ++ <source>contains</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="311"/> ++ <source>name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="311"/> ++ <source>type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="311"/> ++ <source>modify time</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="311"/> ++ <source>file size</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="312"/> ++ <location filename="../../src/control/tab-widget.h" line="314"/> ++ <location filename="../../src/control/tab-widget.h" line="315"/> ++ <source>all</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="312"/> ++ <source>file folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="312"/> ++ <source>image</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="312"/> ++ <source>video</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="313"/> ++ <source>text file</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="313"/> ++ <source>audio</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="313"/> ++ <source>others</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="313"/> ++ <source>wps file</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="314"/> ++ <source>today</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="314"/> ++ <source>this week</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="314"/> ++ <source>this month</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="314"/> ++ <source>this year</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="314"/> ++ <source>yesterday</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="314"/> ++ <source>last week</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="314"/> ++ <source>last month</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="314"/> ++ <source>last year</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>year ago</source> ++ <translation type="vanished">一年前</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="315"/> ++ <source>tiny(0-16K)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="315"/> ++ <source>small(16k-1M)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="315"/> ++ <source>empty(0K)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="315"/> ++ <source>medium(1M-128M)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="315"/> ++ <source>big(128M-1G)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="315"/> ++ <source>large(1-4G)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="315"/> ++ <source>great(>4G)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>medium(1M-100M)</source> ++ <translation type="vanished">中等(1M-100M)</translation> ++ </message> ++ <message> ++ <source>big(100M-1G)</source> ++ <translation type="vanished">很大(100M-1G)</translation> ++ </message> ++ <message> ++ <source>large(>1G)</source> ++ <translation type="vanished">极大(>1G)</translation> ++ </message> ++</context> ++<context> ++ <name>TitleLabel</name> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="989"/> ++ <source>Peony</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>TopMenuBar</name> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="1097"/> ++ <source>Option</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="1110"/> ++ <source>Minimize</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="1133"/> ++ <source>Close</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++</TS> diff -Nru peony-4.10.0.5/debian/patches/0089-Added-translation-using-Weblate-Vietnamese.patch peony-4.10.0.5/debian/patches/0089-Added-translation-using-Weblate-Vietnamese.patch --- peony-4.10.0.5/debian/patches/0089-Added-translation-using-Weblate-Vietnamese.patch 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/patches/0089-Added-translation-using-Weblate-Vietnamese.patch 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,1402 @@ +From: KevinDuan <duankaiwen@kylinos.cn> +Date: Mon, 17 Feb 2025 15:02:17 +0800 +Subject: Added translation using Weblate (Vietnamese) + +--- + translations/peony-qt/peony-qt_vi.ts | 1387 ++++++++++++++++++++++++++++++++++ + 1 file changed, 1387 insertions(+) + create mode 100644 translations/peony-qt/peony-qt_vi.ts + +diff --git a/translations/peony-qt/peony-qt_vi.ts b/translations/peony-qt/peony-qt_vi.ts +new file mode 100644 +index 0000000..268f133 +--- /dev/null ++++ b/translations/peony-qt/peony-qt_vi.ts +@@ -0,0 +1,1387 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<!DOCTYPE TS> ++<TS version="2.1" language="vi"> ++<context> ++ <name>AboutDialog</name> ++ <message> ++ <location filename="../../src/windows/about-dialog.ui" line="32"/> ++ <source>Dialog</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/about-dialog.ui" line="88"/> ++ <source><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> ++<html><head><meta name="qrichtext" content="1" /><style type="text/css"> ++p, li { white-space: pre-wrap; } ++</style></head><body style=" font-family:'Noto Sans CJK SC'; font-size:10pt; font-weight:400; font-style:normal;"> ++<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><br /></p></body></html></source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/about-dialog.ui" line="115"/> ++ <source>TextLabel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Offical Website: </source> ++ <translation type="vanished">官方网站: </translation> ++ </message> ++ <message> ++ <source>Service & Technology Support: </source> ++ <translation type="vanished">服务与技术支持: </translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/about-dialog.cpp" line="47"/> ++ <location filename="../../src/windows/about-dialog.cpp" line="168"/> ++ <source>Service & Support: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/about-dialog.cpp" line="46"/> ++ <location filename="../../src/windows/about-dialog.cpp" line="98"/> ++ <location filename="../../src/windows/about-dialog.cpp" line="114"/> ++ <source>Peony</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>peony</source> ++ <translation type="vanished">文件管理器</translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/about-dialog.cpp" line="51"/> ++ <location filename="../../src/windows/about-dialog.cpp" line="129"/> ++ <source>Peony is a graphical software to help users manage system files. It provides common file operation functions for users, such as file viewing, file copy, paste, cut, delete, rename, file selection, application opening, file search, file sorting, file preview, etc. it is convenient for users to manage system files intuitively on the interface.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Hot Service: </source> ++ <translation type="vanished">服务热线: </translation> ++ </message> ++ <message> ++ <source>File Manager</source> ++ <translation type="vanished">文件管理器</translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/about-dialog.cpp" line="50"/> ++ <location filename="../../src/windows/about-dialog.cpp" line="117"/> ++ <source>Version number: %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>File manager is a graphical software to help users manage system files. It provides common file operation functions for users, such as file viewing, file copy, paste, cut, delete, rename, file selection, application opening, file search, file sorting, file preview, etc. it is convenient for users to manage system files intuitively on the interface.</source> ++ <translation type="vanished">文件管理器是一款帮助用户管理系统文件的图形化的软件,为用户提供常用的文件操作功能,比如文件查看,文件复制、粘贴、剪切、删除、重命名,文件打开方式选择,文件搜索,文件排序,文件预览等,方便用户在界面上直观地管理系统文件。</translation> ++ </message> ++ <message> ++ <source>none</source> ++ <translation type="vanished">无</translation> ++ </message> ++</context> ++<context> ++ <name>FileLabelBox</name> ++ <message> ++ <location filename="../../src/control/file-label-box.cpp" line="72"/> ++ <source>Rename</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/file-label-box.cpp" line="77"/> ++ <source>Edit Color</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Delete</source> ++ <translation type="vanished">删除标记</translation> ++ </message> ++ <message> ++ <source>Create New Label</source> ++ <translation type="vanished">创建标记</translation> ++ </message> ++</context> ++<context> ++ <name>HeaderBar</name> ++ <message> ++ <source>Create Folder</source> ++ <translation type="vanished">新建文件夹</translation> ++ </message> ++ <message> ++ <source>Open Terminal</source> ++ <translation type="vanished">打开终端</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="136"/> ++ <source>Go Back</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="144"/> ++ <source>Go Forward</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Search</source> ++ <translation type="vanished">搜索</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="236"/> ++ <source>View Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="274"/> ++ <source>Sort Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="356"/> ++ <source>Option</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="472"/> ++ <source>Operate Tips</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="163"/> ++ <source>Go Up</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Details</source> ++ <translation type="vanished">详情</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="369"/> ++ <source>&Copy</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="372"/> ++ <source>Copy</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="384"/> ++ <source>&Cut</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="387"/> ++ <source>Cut</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="393"/> ++ <source>&Select All</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="397"/> ++ <location filename="../../src/control/header-bar.cpp" line="409"/> ++ <source>Select All</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="414"/> ++ <location filename="../../src/control/header-bar.cpp" line="926"/> ++ <location filename="../../src/control/header-bar.cpp" line="944"/> ++ <source>Deselect All</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="419"/> ++ <source>&Delete to trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="422"/> ++ <source>Delete to trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="473"/> ++ <source>Don't find any terminal, please install at least one terminal!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="728"/> ++ <location filename="../../src/control/header-bar.cpp" line="929"/> ++ <location filename="../../src/control/header-bar.cpp" line="939"/> ++ <source>Select All Item</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="738"/> ++ <location filename="../../src/control/header-bar.cpp" line="873"/> ++ <location filename="../../src/control/header-bar.cpp" line="912"/> ++ <source>Select</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="713"/> ++ <source>Restore</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="716"/> ++ <source>Maximize</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="915"/> ++ <source>Select Done</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="765"/> ++ <source>MoveTo</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="207"/> ++ <source>Icon View</source> ++ <translatorcomment>图标视图</translatorcomment> ++ <translation></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="217"/> ++ <source>List View</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="774"/> ++ <source>CopyTo</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="783"/> ++ <source>Delete</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="850"/> ++ <source>Select path</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Minimize</source> ++ <translation type="vanished">最小化</translation> ++ </message> ++ <message> ++ <source>Close</source> ++ <translation type="vanished">关闭</translation> ++ </message> ++</context> ++<context> ++ <name>HeaderBarContainer</name> ++ <message> ++ <source>Option</source> ++ <translation type="vanished">选项</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="1295"/> ++ <source>Minimize</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Maximize/Restore</source> ++ <translation type="vanished">最大化/还原</translation> ++ </message> ++ <message> ++ <source>Restore</source> ++ <translation type="vanished">还原</translation> ++ </message> ++ <message> ++ <source>Maximize</source> ++ <translation type="vanished">最大化</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="1307"/> ++ <source>Close</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Intel::NavigationSideBar</name> ++ <message> ++ <location filename="../../src/control/intel/intel-navigation-side-bar.cpp" line="180"/> ++ <source>Open In &New Window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-navigation-side-bar.cpp" line="204"/> ++ <source>Open In New &Tab</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Intel::TitleLabel</name> ++ <message> ++ <location filename="../../src/control/intel/intel-navigation-side-bar.cpp" line="516"/> ++ <source>Files</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>MainWindow</name> ++ <message> ++ <location filename="../../src/windows/main-window.cpp" line="889"/> ++ <source>File Manager</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/main-window.cpp" line="411"/> ++ <source>Undo</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/main-window.cpp" line="418"/> ++ <source>Redo</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/main-window.cpp" line="790"/> ++ <source>warn</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/main-window.cpp" line="790"/> ++ <source>This operation is not supported.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/main-window.cpp" line="887"/> ++ <source>Search</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Tips info</source> ++ <translation type="vanished">温馨提示</translation> ++ </message> ++ <message> ++ <source>Trash has no file need to be cleaned.</source> ++ <translation type="vanished">回收站没有文件需要被清空!</translation> ++ </message> ++ <message> ++ <source>Delete file Warning</source> ++ <translation type="vanished">删除文件警告</translation> ++ </message> ++ <message> ++ <source>Delete Permanently</source> ++ <translation type="vanished">永久删除</translation> ++ </message> ++ <message> ++ <source>Are you sure that you want to delete these files? Once you start a deletion, the files deleting will never be restored again.</source> ++ <translation type="vanished">您确定要删除这些文件吗?一旦开始删除,这些文件将不可再恢复。</translation> ++ </message> ++ <message> ++ <source>Peony Qt</source> ++ <translation type="vanished">文件管理器</translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/main-window.cpp" line="902"/> ++ <source>New Folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>NavigationSideBar</name> ++ <message> ++ <source>All tags...</source> ++ <translation type="vanished">所有标记...</translation> ++ </message> ++ <message> ++ <source>Open In &New Window</source> ++ <translation type="vanished">在新窗口中打开(&N)</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="590"/> ++ <source>warn</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="590"/> ++ <source>This operation is not supported.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="224"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="628"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="646"/> ++ <source>Tips</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="224"/> ++ <source>The device is in busy state, please perform this operation later.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="628"/> ++ <source>This is an abnormal Udisk, please fix it or format it</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="646"/> ++ <source>This is an empty drive, please insert a Disc.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="259"/> ++ <source>Open In New Window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="275"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="309"/> ++ <source>Can not open %1, %2</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="293"/> ++ <source>Open In New Tab</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Open In New &Tab</source> ++ <translation type="vanished">在新标签页中打开(&T)</translation> ++ </message> ++</context> ++<context> ++ <name>NavigationSideBarContainer</name> ++ <message> ++ <source>All tags...</source> ++ <translation type="vanished">所有标记...</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="848"/> ++ <source>Manager tags...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="851"/> ++ <source>More tags...</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>NavigationTabBar</name> ++ <message> ++ <source>Computer</source> ++ <translation type="vanished">计算机</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/navigation-tab-bar.cpp" line="131"/> ++ <source>Search "%1" in "%2"</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>OperationMenu</name> ++ <message> ++ <source>Advance Search</source> ++ <translation type="vanished">高级搜索</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="78"/> ++ <source>Keep Allow</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="90"/> ++ <source>Show Hidden</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="98"/> ++ <source>Show File Extension</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="103"/> ++ <source>Show Create Time</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="110"/> ++ <source>Show Relative Time</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="123"/> ++ <source>Forbid thumbnailing</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="133"/> ++ <source>Resident in Backend</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="142"/> ++ <source>Parallel Operations</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="148"/> ++ <source>Set samba password</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="162"/> ++ <source>Tips</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="162"/> ++ <source>The user already has a samba password, do you need to reset the samba password?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="173"/> ++ <source>Samba set user password</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="171"/> ++ <source>Samba password:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="181"/> ++ <location filename="../../src/control/operation-menu.cpp" line="192"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="181"/> ++ <source>Samba set password failed, Please re-enter!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="192"/> ++ <source>Shared configuration service exception, please confirm if there is an ongoing shared configuration operation, or please reset the share!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="201"/> ++ <source>Open each folder in a new window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="207"/> ++ <source>Plugin manager Settings</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="216"/> ++ <source>Help</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="220"/> ++ <source>About</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>OperationMenuEditWidget</name> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="284"/> ++ <source>Edit</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="295"/> ++ <source>copy</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="304"/> ++ <source>paste</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="313"/> ++ <source>cut</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="322"/> ++ <source>trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::Intel::SideBarCloudItem</name> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-cloud-item.cpp" line="40"/> ++ <source>CloudStorage</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-cloud-item.cpp" line="55"/> ++ <source>CloudFile</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::Intel::SideBarFavoriteItem</name> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-favorite-item.cpp" line="46"/> ++ <source>Favorites</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::Intel::SideBarFileSystemItem</name> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="60"/> ++ <source>Computer</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="79"/> ++ <source>文件系统</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="80"/> ++ <source>System Disk</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::Intel::SideBarMenu</name> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="52"/> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="71"/> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="97"/> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="112"/> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="174"/> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="242"/> ++ <source>Properties</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="82"/> ++ <source>Delete Symbolic</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="132"/> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="234"/> ++ <source>Unmount</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="151"/> ++ <source>Eject</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-menu.cpp" line="203"/> ++ <source>format</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::Intel::SideBarPersonalItem</name> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-personal-item.cpp" line="45"/> ++ <source>Personal</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::Intel::SideBarSeparatorItem</name> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-separator-item.h" line="70"/> ++ <source>(No Sub Directory)</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::Intel::SideBarUserDiskItem</name> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="722"/> ++ <source>User Disk</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::Intel::TabletSideBarFactory</name> ++ <message> ++ <location filename="../../src/control/intel/tablet-side-bar-factory.cpp" line="60"/> ++ <source>Intel Side Bar</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SearchWidget</name> ++ <message> ++ <location filename="../../src/control/search-widget.cpp" line="50"/> ++ <source>Search</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::TrashWarnDialog</name> ++ <message> ++ <location filename="../../src/windows/trash-warn-dialog.cpp" line="101"/> ++ <source>Are you sure that you want to delete these files? Once you start a deletion, the files deleting will never be restored again.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/trash-warn-dialog.cpp" line="114"/> ++ <source>Delete</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/windows/trash-warn-dialog.cpp" line="115"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>PeonyApplication</name> ++ <message> ++ <source>Peony-Qt</source> ++ <translation type="vanished">文件管理器</translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.cpp" line="170"/> ++ <source>peony-qt</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.cpp" line="177"/> ++ <source>Files or directories to open</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.cpp" line="177"/> ++ <source>[FILE1, FILE2,...]</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.cpp" line="224"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.cpp" line="224"/> ++ <source>Peony-Qt can not get the system's icon theme. There are 2 reasons might lead to this problem: ++ ++1. Peony-Qt might be running as root, that means you have the higher permission and can do some things which normally forbidden. But, you should learn that if you were in a root, the virtual file system will lose some featrue such as you can not use "My Computer", the theme and icons might also went wrong. So, run peony-qt in a root is not recommended. ++ ++2. You are using a non-qt theme for your system but you didn't install the platform theme plugin for qt's applications. If you are using gtk-theme, try installing the qt5-gtk2-platformtheme package to resolve this problem.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.cpp" line="495"/> ++ <source>Peony Qt</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.cpp" line="496"/> ++ <source>Author: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2020, KylinSoft Co., Ltd.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Author: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019-2020, KYLIN Software Co., Ltd.</source> ++ <translation type="vanished">作者: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++版权所有(C): 2019-2020,麒麟软件有限公司.</translation> ++ </message> ++ <message> ++ <source>Author: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019-2020, KYLIN Software Co., Ltd.</source> ++ <translation type="vanished">作者: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++版权所有(C): 2019-2020,麒麟软件有限公司.</translation> ++ </message> ++ <message> ++ <source>Authour: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019-2020, KYLIN Software Co., Ltd.</source> ++ <translation type="vanished">作者: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++版权所有(C): 2019-2020,麒麟软件有限公司.</translation> ++ </message> ++ <message> ++ <source>Authour: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019-2020, Tianjin KYLIN Information Technology Co., Ltd.</source> ++ <translation type="vanished">作者: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++版权所有(C): 2019-2020,天津麒麟信息技术有限公司.</translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.h" line="67"/> ++ <source>Close all peony-qt windows and quit</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.h" line="68"/> ++ <source>Show items</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.h" line="69"/> ++ <source>Show folders</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.h" line="70"/> ++ <source>Show properties</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>QObject</name> ++ <message> ++ <source>Error</source> ++ <translation type="vanished">错误</translation> ++ </message> ++ <message> ++ <source>Can not open %1.</source> ++ <translation type="vanished">无法打开 %1.</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="451"/> ++ <source>Force unmount failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="451"/> ++ <source>Error: %1 ++</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="455"/> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="494"/> ++ <source>Data synchronization is complete,the device has been unmount successfully!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="473"/> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="478"/> ++ <source>Unmount failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="473"/> ++ <source>Unable to unmount it, you may need to close some programs, such as: GParted etc.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="478"/> ++ <source>Error: %1 ++Do you want to unmount forcely?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="630"/> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="684"/> ++ <source>Eject failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="631"/> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="685"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="632"/> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="686"/> ++ <source>Eject Anyway</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="644"/> ++ <source>Data synchronization is complete and the device can be safely unplugged!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="682"/> ++ <source>Unable to eject %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="700"/> ++ <source>PeonyNotify</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/intel/intel-side-bar-file-system-item.cpp" line="701"/> ++ <source>File Manager</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>SortTypeMenu</name> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="34"/> ++ <source>File Name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="46"/> ++ <source>File Size</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="50"/> ++ <source>Original Path</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="82"/> ++ <source>Use current sorting for all folders</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="102"/> ++ <source>By %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="111"/> ++ <source>Newest to oldest</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="112"/> ++ <source>Oldest to newest</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="114"/> ++ <source>Files from large to small</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="115"/> ++ <source>Files from small to large</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Use global sorting</source> ++ <translation type="vanished">使用全局排序</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="42"/> ++ <source>File Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="38"/> ++ <source>Modified Date</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Modified Data</source> ++ <translation type="vanished">修改日期</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="71"/> ++ <location filename="../../src/control/sort-type-menu.cpp" line="118"/> ++ <source>Ascending</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="66"/> ++ <location filename="../../src/control/sort-type-menu.cpp" line="117"/> ++ <source>Descending</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>TabStatusBar</name> ++ <message> ++ <source>Current path has:</source> ++ <translation type="vanished">当前路径包含:</translation> ++ </message> ++ <message> ++ <source>%1 folders, %2 files</source> ++ <translation type="vanished">%1 文件夹,%2 文件</translation> ++ </message> ++ <message> ++ <source>%1 folders</source> ++ <translation type="vanished">%1 文件夹</translation> ++ </message> ++ <message> ++ <source>%1 files</source> ++ <translation type="vanished">%1 文件</translation> ++ </message> ++ <message> ++ <source>; %1 folders</source> ++ <translation type="vanished">; %1 个文件夹</translation> ++ </message> ++ <message> ++ <source>; %1 files, %2 total</source> ++ <translation type="vanished">; %1 个文件, 共%2</translation> ++ </message> ++ <message> ++ <source>; %1 folder</source> ++ <translation type="vanished">; %1 个文件夹</translation> ++ </message> ++ <message> ++ <source>; %1 file, %2</source> ++ <translation type="vanished">; %1 个文件, %2</translation> ++ </message> ++ <message> ++ <source>%1 selected</source> ++ <translation type="vanished">选中%1个</translation> ++ </message> ++ <message> ++ <source>Search "%1" in "%2"</source> ++ <translation type="vanished">在%2中搜索%1</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-status-bar.cpp" line="122"/> ++ <source> %1 items </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-status-bar.cpp" line="109"/> ++ <location filename="../../src/control/tab-status-bar.cpp" line="218"/> ++ <source>Searching for files ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>selected%1%2</source> ++ <translation type="vanished">选中%1%2</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-status-bar.cpp" line="159"/> ++ <source> selected %1 items %2</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source> %1 items selected %2 items</source> ++ <translation type="vanished">%1 项 选中 %2 项</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-status-bar.cpp" line="161"/> ++ <source> selected %1 items</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>TabWidget</name> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="285"/> ++ <source>Trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="289"/> ++ <source>Clear</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="294"/> ++ <source>Recover</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="451"/> ++ <source>Computer</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Close Filter.</source> ++ <translation type="vanished">关闭筛选。</translation> ++ </message> ++ <message> ++ <source>Filter</source> ++ <translation type="vanished">筛选</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="526"/> ++ <source>Select Path</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="1347"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="1349"/> ++ <source>Error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="1708"/> ++ <source>Opening such files is not currently supported</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="1722"/> ++ <source>Open failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="1723"/> ++ <source>Open directory failed, you have no permission!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Close advance search.</source> ++ <translation type="vanished">关闭高级搜索。</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="441"/> ++ <source>Search</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Choose other path to search.</source> ++ <translation type="vanished">选择其他搜索路径。</translation> ++ </message> ++ <message> ++ <source>Search recursively</source> ++ <translation type="vanished">递归搜索</translation> ++ </message> ++ <message> ++ <source>more options</source> ++ <translation type="vanished">更多选项</translation> ++ </message> ++ <message> ++ <source>Show/hide advance search</source> ++ <translation type="vanished">显示/隐藏高级搜索</translation> ++ </message> ++ <message> ++ <source>Select path</source> ++ <translation type="vanished">选择路径</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="586"/> ++ <location filename="../../src/control/tab-widget.cpp" line="746"/> ++ <source>is</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="621"/> ++ <source>Please input key words...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Please input kay words...</source> ++ <translation type="vanished">请输入关键词...</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="707"/> ++ <location filename="../../src/control/tab-widget.cpp" line="730"/> ++ <source>contains</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="311"/> ++ <source>name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="311"/> ++ <source>type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="311"/> ++ <source>modify time</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="311"/> ++ <source>file size</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="312"/> ++ <location filename="../../src/control/tab-widget.h" line="314"/> ++ <location filename="../../src/control/tab-widget.h" line="315"/> ++ <source>all</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="312"/> ++ <source>file folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="312"/> ++ <source>image</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="312"/> ++ <source>video</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="313"/> ++ <source>text file</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="313"/> ++ <source>audio</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="313"/> ++ <source>others</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="313"/> ++ <source>wps file</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="314"/> ++ <source>today</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="314"/> ++ <source>this week</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="314"/> ++ <source>this month</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="314"/> ++ <source>this year</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="314"/> ++ <source>yesterday</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="314"/> ++ <source>last week</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="314"/> ++ <source>last month</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="314"/> ++ <source>last year</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>year ago</source> ++ <translation type="vanished">一年前</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="315"/> ++ <source>tiny(0-16K)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="315"/> ++ <source>small(16k-1M)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="315"/> ++ <source>empty(0K)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="315"/> ++ <source>medium(1M-128M)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="315"/> ++ <source>big(128M-1G)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="315"/> ++ <source>large(1-4G)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="315"/> ++ <source>great(>4G)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>medium(1M-100M)</source> ++ <translation type="vanished">中等(1M-100M)</translation> ++ </message> ++ <message> ++ <source>big(100M-1G)</source> ++ <translation type="vanished">很大(100M-1G)</translation> ++ </message> ++ <message> ++ <source>large(>1G)</source> ++ <translation type="vanished">极大(>1G)</translation> ++ </message> ++</context> ++<context> ++ <name>TitleLabel</name> ++ <message> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="989"/> ++ <source>Peony</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>TopMenuBar</name> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="1097"/> ++ <source>Option</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="1110"/> ++ <source>Minimize</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/header-bar.cpp" line="1133"/> ++ <source>Close</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++</TS> diff -Nru peony-4.10.0.5/debian/patches/0090-.patch peony-4.10.0.5/debian/patches/0090-.patch --- peony-4.10.0.5/debian/patches/0090-.patch 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/patches/0090-.patch 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,34545 @@ +From: Yue-Lan <lanyue@kylinos.cn> +Date: Mon, 24 Feb 2025 16:58:41 +0800 +Subject: =?utf-8?b?5pu05paw5Lit5paH57+76K+R?= + +--- + 3rd-parties/layouts/flowlayout.cpp | 230 + + 3rd-parties/layouts/flowlayout.h | 89 + + 3rd-parties/layouts/layouts.pri | 5 + + common.pri | 7 + + libpeony-qt/connect-to-server-dialog.cpp | 95 +- + libpeony-qt/connect-to-server-dialog.h | 9 + + libpeony-qt/controls/controls.pri | 2 + + .../directory-view/delegate/icon-view-delegate.cpp | 72 +- + .../directory-view/delegate/icon-view-delegate.h | 9 +- + .../delegate/icon-view-index-widget.cpp | 152 +- + .../directory-view/delegate/list-view-delegate.cpp | 119 +- + .../directory-view/delegate/list-view-delegate.h | 19 +- + .../directory-view/directory-view-container.cpp | 75 +- + .../directory-view/directory-view-container.h | 13 + + .../directory-view/view/icon-view/icon-view.cpp | 26 +- + .../view/list-view/list-view-style.cpp | 1 - + .../view/list-view/list-view-style.h | 1 - + .../directory-view/view/list-view/list-view.cpp | 82 +- + .../directory-view/view/list-view/list-view.h | 1 + + .../directory-view-menu/directory-view-menu.cpp | 215 +- + libpeony-qt/controls/menu/menu-plugin-manager.cpp | 27 +- + libpeony-qt/controls/menu/menu-plugin-manager.h | 6 + + .../controls/menu/side-bar-menu/side-bar-menu.cpp | 77 +- + .../controls/menu/side-bar-menu/side-bar-menu.h | 1 + + libpeony-qt/controls/multi-select-combobox.cpp | 445 ++ + libpeony-qt/controls/multi-select-combobox.h | 113 + + .../navigation-bar/advanced-location-bar.cpp | 60 +- + .../navigation-bar/advanced-location-bar.h | 6 + + .../navigation-bar/location-bar/location-bar.cpp | 233 +- + .../navigation-bar/location-bar/location-bar.h | 14 + + .../default-preview-page/default-preview-page.cpp | 19 +- + .../default-preview-page/default-preview-page.h | 1 + + .../property-page/basic-properties-page.cpp | 524 +- + .../controls/property-page/basic-properties-page.h | 52 +- + .../property-page/computer-properties-page.cpp | 161 +- + .../property-page/computer-properties-page.h | 11 + + .../property-page/open-with-properties-page.cpp | 18 +- + .../controls/tool-bar/search-bar-container.cpp | 101 +- + .../controls/tool-bar/search-bar-container.h | 7 +- + libpeony-qt/convenient-utils/clipboard-utils.cpp | 16 +- + libpeony-qt/convenient-utils/convenient-utils.cpp | 82 + + libpeony-qt/convenient-utils/convenient-utils.h | 28 + + libpeony-qt/convenient-utils/convenient-utils.pri | 2 + + libpeony-qt/convenient-utils/datacdrom.cpp | 93 +- + libpeony-qt/convenient-utils/disc/disccontrol.cpp | 16 +- + .../convenient-utils/file-operation-utils.cpp | 78 +- + .../convenient-utils/file-operation-utils.h | 4 + + libpeony-qt/extensions-manager-widget.cpp | 46 +- + libpeony-qt/extensions-manager-widget.h | 12 +- + libpeony-qt/file-copy.cpp | 7 +- + libpeony-qt/file-info-job.cpp | 24 +- + libpeony-qt/file-info.cpp | 100 +- + libpeony-qt/file-info.h | 21 +- + libpeony-qt/file-infos-job.cpp | 33 +- + libpeony-qt/file-launcher/file-lauch-dialog.cpp | 29 +- + libpeony-qt/file-launcher/file-launch-action.cpp | 26 +- + libpeony-qt/file-launcher/file-launch-manager.cpp | 50 +- + libpeony-qt/file-launcher/file-launch-manager.h | 4 + + .../file-operation/create-template-operation.cpp | 52 +- + libpeony-qt/file-operation/file-copy-operation.cpp | 200 +- + libpeony-qt/file-operation/file-copy-operation.h | 10 + + .../file-operation/file-count-operation.cpp | 4 +- + libpeony-qt/file-operation/file-count-operation.h | 5 + + .../file-operation/file-delete-operation.cpp | 7 +- + libpeony-qt/file-operation/file-move-operation.cpp | 196 +- + libpeony-qt/file-operation/file-move-operation.h | 9 + + libpeony-qt/file-operation/file-node-reporter.h | 10 +- + libpeony-qt/file-operation/file-node.cpp | 33 +- + .../file-operation-dialog/kyfiledialogrename.cpp | 51 +- + .../file-operation-dialog/kyfiledialogrename.h | 3 + + .../file-operation-error-dialog-base.cpp | 30 +- + .../file-operation-error-dialogs.cpp | 60 +- + .../file-operation/file-operation-error-dialogs.h | 1 + + .../file-operation/file-operation-helper.cpp | 48 +- + libpeony-qt/file-operation/file-operation-helper.h | 4 + + .../file-operation/file-operation-manager.cpp | 97 +- + .../file-operation/file-operation-manager.h | 7 +- + .../file-operation-progress-bar-helper.cpp | 56 + + .../file-operation-progress-bar-helper.h | 15 + + .../file-operation/file-operation-progress-bar.cpp | 84 +- + .../file-operation/file-operation-progress-bar.h | 21 +- + libpeony-qt/file-operation/file-operation.cpp | 55 + + libpeony-qt/file-operation/file-operation.h | 5 + + libpeony-qt/file-operation/file-operation.pri | 4 + + .../file-operation/file-properties-operation.cpp | 297 ++ + .../file-operation/file-properties-operation.h | 73 + + .../file-operation/file-rename-operation.cpp | 27 +- + .../file-operation/file-trash-operation.cpp | 102 +- + .../file-operation/file-untrash-operation.cpp | 17 +- + libpeony-qt/file-utils.cpp | 214 +- + libpeony-qt/file-utils.h | 28 +- + libpeony-qt/file-watcher.cpp | 42 +- + libpeony-qt/file-watcher.h | 1 + + libpeony-qt/global-fstabdata.h | 2 +- + libpeony-qt/global-settings.cpp | 254 +- + libpeony-qt/global-settings.h | 97 +- + libpeony-qt/libpeony-qt.pro | 3 +- + libpeony-qt/model/file-item-model.cpp | 116 +- + libpeony-qt/model/file-item-model.h | 8 +- + .../model/file-item-proxy-filter-sort-model.cpp | 258 +- + .../model/file-item-proxy-filter-sort-model.h | 26 + + libpeony-qt/model/file-item.cpp | 64 +- + libpeony-qt/model/file-item.h | 1 + + libpeony-qt/model/file-label-model.cpp | 19 +- + libpeony-qt/model/file-label-model.h | 2 + + libpeony-qt/model/side-bar-favorite-item.cpp | 5 +- + libpeony-qt/model/side-bar-file-system-item.cpp | 16 +- + libpeony-qt/model/side-bar-net-work-item.cpp | 16 + + libpeony-qt/model/side-bar-net-work-item.h | 1 + + libpeony-qt/mount-operation.cpp | 59 +- + libpeony-qt/org.ukui.peony.settings.gschema.xml | 36 + + libpeony-qt/peony-core.pri | 2 + + libpeony-qt/plugin-manager.cpp | 60 +- + libpeony-qt/plugin-manager.h | 5 + + .../properties-window-factory-plugin-manager.cpp | 14 +- + .../properties-window-factory-plugin-manager.h | 4 +- + libpeony-qt/sound-effect.cpp | 9 +- + libpeony-qt/sync-thread.cpp | 2 +- + libpeony-qt/thumbnail-manager.cpp | 102 +- + libpeony-qt/thumbnail-manager.h | 2 + + libpeony-qt/thumbnail/generic-thumbnailer.cpp | 12 +- + libpeony-qt/thumbnail/image-pdf-thumbnail.cpp | 3 +- + libpeony-qt/thumbnail/office-thumbnail.cpp | 5 +- + libpeony-qt/thumbnail/text-plain-thumbnail.cpp | 255 + + libpeony-qt/thumbnail/text-plain-thumbnail.h | 56 + + libpeony-qt/thumbnail/thumbnail-job.cpp | 38 +- + libpeony-qt/thumbnail/thumbnail-job.h | 2 + + libpeony-qt/thumbnail/thumbnail.pri | 2 + + libpeony-qt/thumbnail/video-thumbnail.cpp | 105 +- + libpeony-qt/thumbnail/video-thumbnail.h | 9 + + libpeony-qt/tooltips-manager.cpp | 275 + + libpeony-qt/tooltips-manager.h | 109 + + libpeony-qt/usershare-manager.cpp | 16 + + libpeony-qt/usershare-manager.h | 1 + + libpeony-qt/vfs/favorite-vfs-file-enumerator.cpp | 1 + + libpeony-qt/vfs/label-vfs-file-monitor.cpp | 15 + + libpeony-qt/vfs/label-vfs-file-monitor.h | 3 + + libpeony-qt/vfs/label-vfs-file.cpp | 28 +- + .../vfs/peony-search-vfs-file-enumerator.cpp | 57 +- + libpeony-qt/vfs/peony-search-vfs-file-enumerator.h | 13 +- + libpeony-qt/vfs/peony-search-vfs-file.cpp | 23 +- + libpeony-qt/vfs/recent-vfs-manager.cpp | 5 + + libpeony-qt/vfs/recent-vfs-manager.h | 7 + + libpeony-qt/vfs/search-vfs-uri-parser.cpp | 15 + + libpeony-qt/vfs/search-vfs-uri-parser.h | 1 + + libpeony-qt/volumeManager.cpp | 91 +- + libpeony-qt/volumeManager.h | 7 +- + libpeony-qt/windows/format-dlg-create-delegate.cpp | 1 + + libpeony-qt/windows/format_dialog.cpp | 131 +- + libpeony-qt/windows/format_dialog.h | 1 + + libpeony-qt/windows/ky-udf-format-dialog.cpp | 2 + + libpeony-qt/windows/properties-window-factory.cpp | 13 +- + libpeony-qt/windows/properties-window-factory.h | 7 +- + libpeony-qt/windows/properties-window.cpp | 103 +- + libpeony-qt/windows/properties-window.h | 7 +- + .../properties-window-factory-plugin-iface.h | 3 +- + src/control/file-label-box.cpp | 31 +- + src/control/header-bar.cpp | 14 +- + src/control/navigation-side-bar.cpp | 92 +- + src/control/navigation-tab-bar.cpp | 56 +- + src/control/operation-menu.cpp | 108 +- + src/control/operation-menu.h | 21 + + src/control/search-widget.cpp | 118 +- + src/control/search-widget.h | 6 +- + src/control/sort-type-menu.cpp | 9 + + src/control/sort-type-menu.h | 3 + + src/control/tab-widget.cpp | 763 ++- + src/control/tab-widget.h | 70 +- + src/peony-application.cpp | 19 +- + src/src.pro | 1 + + src/windows/main-window.cpp | 155 +- + src/windows/main-window.h | 1 + + .../properties-window-factory.cpp | 13 +- + .../properties-window/properties-window-factory.h | 13 +- + stable/ukui3/windows/main-window.cpp | 3 +- + .../file-manager-operation.cpp | 167 + + .../file-manager-operation.pro | 18 + + tests/kt-gtest/file-manager-operation/main.cpp | 8 + + .../kt-gtest/kt-desktop-gtest/kt-desktop-gtest.pro | 21 + + tests/kt-gtest/kt-desktop-gtest/main.cpp | 9 + + .../kt-gtest/kt-desktop-gtest/tst_desktoptest.cpp | 25 + + tests/kt-gtest/kt-gtest.pro | 6 + + .../kt-core-gtest/kt-core-gtest.pro | 7 + + .../kt-libpeony-gtest/kt-core-gtest/main.cpp | 8 + + .../kt-core-gtest/tst_core_gtest.cpp | 84 + + .../kt-libpeony-gtest/kt-libpeony-gtest.pri | 47 + + .../kt-libpeony-gtest/kt-libpeony-gtest.pro | 8 + + .../kt-model-gtest/kt-model-gtest.pro | 8 + + .../kt-libpeony-gtest/kt-model-gtest/main.cpp | 8 + + .../kt-libpeony-gtest/kt-model-gtest/tst_model.cpp | 98 + + .../kt-operation-gtest/kt-operation-gtest.pro | 7 + + .../kt-libpeony-gtest/kt-operation-gtest/main.cpp | 8 + + .../kt-operation-gtest/tst-copy-operation-test.cpp | 22 + + .../kt-operation-gtest/tst_operation.cpp | 112 + + .../kt-plugin-iface-gtest.pro | 6 + + .../kt-plugin-iface-gtest/main.cpp | 8 + + .../kt-plugin-iface-gtest/tst_plugin_iface.cpp | 19 + + .../kt-operation-gtest/kt-operation-gtest.pro | 21 + + tests/kt-gtest/kt-operation-gtest/main.cpp | 8 + + .../tst-operation-progress-bar-gtest.cpp | 44 + + .../kt-operation-gtest/tst_operationtest.cpp | 54 + + .../kt-operation-menu-gtest.pro | 14 + + tests/kt-gtest/kt-operation-menu-gtest/main.cpp | 9 + + .../tst_operation-menu-test.cpp | 27 + + .../kt-peony-model-gtest/kt-peony-model-gtest.pro | 15 + + tests/kt-gtest/kt-peony-model-gtest/main.cpp | 8 + + .../kt-peony-model-gtest/peony-model-gtest.cpp | 44 + + tests/kt-gtest/kt-peony-model-gtest/tst_test.h | 15 + + tests/kt-gtest/kt-src-gtest/kt-src-gtest.pri | 56 + + tests/kt-gtest/kt-src-gtest/kt-src-gtest.pro | 14 + + tests/kt-gtest/kt-src-gtest/main.cpp | 8 + + tests/kt-gtest/kt-src-gtest/tst_uitest.cpp | 48 + + tests/kt-gtest/kt-test-utils/cpp-stub/addr_any.h | 280 + + tests/kt-gtest/kt-test-utils/cpp-stub/addr_pri.h | 177 + + tests/kt-gtest/kt-test-utils/cpp-stub/elfio.hpp | 4888 +++++++++++++++++ + tests/kt-gtest/kt-test-utils/cpp-stub/stub.h | 360 ++ + .../kt-test-utils/stub-ext/stub-shadow.cpp | 31 + + .../kt-gtest/kt-test-utils/stub-ext/stub-shadow.h | 145 + + tests/kt-gtest/kt-test-utils/stub-ext/stubext.h | 129 + + tests/kt-gtest/kt-test.pro | 8 + + tests/kt-gtest/test-utils.pri | 21 + + tests/tests.pro | 5 + + translations/libpeony-qt/libpeony-qt_vi.ts | 5539 ++++++++++++++++++++ + translations/libpeony-qt/libpeony-qt_zh_CN.ts | 1647 +++--- + .../peony-qt-desktop/peony-qt-desktop_zh_CN.ts | 38 +- + translations/peony-qt/peony-qt_zh_CN.ts | 318 +- + 226 files changed, 21854 insertions(+), 2559 deletions(-) + create mode 100644 3rd-parties/layouts/flowlayout.cpp + create mode 100644 3rd-parties/layouts/flowlayout.h + create mode 100644 3rd-parties/layouts/layouts.pri + create mode 100644 libpeony-qt/controls/multi-select-combobox.cpp + create mode 100644 libpeony-qt/controls/multi-select-combobox.h + create mode 100644 libpeony-qt/convenient-utils/convenient-utils.cpp + create mode 100644 libpeony-qt/convenient-utils/convenient-utils.h + create mode 100644 libpeony-qt/file-operation/file-operation-progress-bar-helper.cpp + create mode 100644 libpeony-qt/file-operation/file-operation-progress-bar-helper.h + create mode 100644 libpeony-qt/file-operation/file-properties-operation.cpp + create mode 100644 libpeony-qt/file-operation/file-properties-operation.h + create mode 100644 libpeony-qt/thumbnail/text-plain-thumbnail.cpp + create mode 100644 libpeony-qt/thumbnail/text-plain-thumbnail.h + create mode 100644 libpeony-qt/tooltips-manager.cpp + create mode 100644 libpeony-qt/tooltips-manager.h + create mode 100644 tests/kt-gtest/file-manager-operation/file-manager-operation.cpp + create mode 100644 tests/kt-gtest/file-manager-operation/file-manager-operation.pro + create mode 100644 tests/kt-gtest/file-manager-operation/main.cpp + create mode 100644 tests/kt-gtest/kt-desktop-gtest/kt-desktop-gtest.pro + create mode 100644 tests/kt-gtest/kt-desktop-gtest/main.cpp + create mode 100644 tests/kt-gtest/kt-desktop-gtest/tst_desktoptest.cpp + create mode 100644 tests/kt-gtest/kt-gtest.pro + create mode 100644 tests/kt-gtest/kt-libpeony-gtest/kt-core-gtest/kt-core-gtest.pro + create mode 100644 tests/kt-gtest/kt-libpeony-gtest/kt-core-gtest/main.cpp + create mode 100644 tests/kt-gtest/kt-libpeony-gtest/kt-core-gtest/tst_core_gtest.cpp + create mode 100644 tests/kt-gtest/kt-libpeony-gtest/kt-libpeony-gtest.pri + create mode 100644 tests/kt-gtest/kt-libpeony-gtest/kt-libpeony-gtest.pro + create mode 100644 tests/kt-gtest/kt-libpeony-gtest/kt-model-gtest/kt-model-gtest.pro + create mode 100644 tests/kt-gtest/kt-libpeony-gtest/kt-model-gtest/main.cpp + create mode 100644 tests/kt-gtest/kt-libpeony-gtest/kt-model-gtest/tst_model.cpp + create mode 100644 tests/kt-gtest/kt-libpeony-gtest/kt-operation-gtest/kt-operation-gtest.pro + create mode 100644 tests/kt-gtest/kt-libpeony-gtest/kt-operation-gtest/main.cpp + create mode 100644 tests/kt-gtest/kt-libpeony-gtest/kt-operation-gtest/tst-copy-operation-test.cpp + create mode 100644 tests/kt-gtest/kt-libpeony-gtest/kt-operation-gtest/tst_operation.cpp + create mode 100644 tests/kt-gtest/kt-libpeony-gtest/kt-plugin-iface-gtest/kt-plugin-iface-gtest.pro + create mode 100644 tests/kt-gtest/kt-libpeony-gtest/kt-plugin-iface-gtest/main.cpp + create mode 100644 tests/kt-gtest/kt-libpeony-gtest/kt-plugin-iface-gtest/tst_plugin_iface.cpp + create mode 100644 tests/kt-gtest/kt-operation-gtest/kt-operation-gtest.pro + create mode 100644 tests/kt-gtest/kt-operation-gtest/main.cpp + create mode 100644 tests/kt-gtest/kt-operation-gtest/tst-operation-progress-bar-gtest.cpp + create mode 100644 tests/kt-gtest/kt-operation-gtest/tst_operationtest.cpp + create mode 100644 tests/kt-gtest/kt-operation-menu-gtest/kt-operation-menu-gtest.pro + create mode 100644 tests/kt-gtest/kt-operation-menu-gtest/main.cpp + create mode 100644 tests/kt-gtest/kt-operation-menu-gtest/tst_operation-menu-test.cpp + create mode 100644 tests/kt-gtest/kt-peony-model-gtest/kt-peony-model-gtest.pro + create mode 100644 tests/kt-gtest/kt-peony-model-gtest/main.cpp + create mode 100644 tests/kt-gtest/kt-peony-model-gtest/peony-model-gtest.cpp + create mode 100644 tests/kt-gtest/kt-peony-model-gtest/tst_test.h + create mode 100644 tests/kt-gtest/kt-src-gtest/kt-src-gtest.pri + create mode 100644 tests/kt-gtest/kt-src-gtest/kt-src-gtest.pro + create mode 100644 tests/kt-gtest/kt-src-gtest/main.cpp + create mode 100644 tests/kt-gtest/kt-src-gtest/tst_uitest.cpp + create mode 100755 tests/kt-gtest/kt-test-utils/cpp-stub/addr_any.h + create mode 100755 tests/kt-gtest/kt-test-utils/cpp-stub/addr_pri.h + create mode 100755 tests/kt-gtest/kt-test-utils/cpp-stub/elfio.hpp + create mode 100755 tests/kt-gtest/kt-test-utils/cpp-stub/stub.h + create mode 100755 tests/kt-gtest/kt-test-utils/stub-ext/stub-shadow.cpp + create mode 100755 tests/kt-gtest/kt-test-utils/stub-ext/stub-shadow.h + create mode 100755 tests/kt-gtest/kt-test-utils/stub-ext/stubext.h + create mode 100644 tests/kt-gtest/kt-test.pro + create mode 100644 tests/kt-gtest/test-utils.pri + create mode 100644 tests/tests.pro + create mode 100644 translations/libpeony-qt/libpeony-qt_vi.ts + +diff --git a/3rd-parties/layouts/flowlayout.cpp b/3rd-parties/layouts/flowlayout.cpp +new file mode 100644 +index 0000000..2af2cc0 +--- /dev/null ++++ b/3rd-parties/layouts/flowlayout.cpp +@@ -0,0 +1,230 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2016 The Qt Company Ltd. ++** Contact: https://www.qt.io/licensing/ ++** ++** This file is part of the examples of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:BSD$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and The Qt Company. For licensing terms ++** and conditions see https://www.qt.io/terms-conditions. For further ++** information use the contact form at https://www.qt.io/contact-us. ++** ++** BSD License Usage ++** Alternatively, you may use this file under the terms of the BSD license ++** as follows: ++** ++** "Redistribution and use in source and binary forms, with or without ++** modification, are permitted provided that the following conditions are ++** met: ++** * Redistributions of source code must retain the above copyright ++** notice, this list of conditions and the following disclaimer. ++** * Redistributions in binary form must reproduce the above copyright ++** notice, this list of conditions and the following disclaimer in ++** the documentation and/or other materials provided with the ++** distribution. ++** * Neither the name of The Qt Company Ltd nor the names of its ++** contributors may be used to endorse or promote products derived ++** from this software without specific prior written permission. ++** ++** ++** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#include <QtWidgets> ++ ++#include "flowlayout.h" ++//! [1] ++FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) ++ : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing) ++{ ++ setContentsMargins(margin, margin, margin, margin); ++} ++ ++FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) ++ : m_hSpace(hSpacing), m_vSpace(vSpacing) ++{ ++ setContentsMargins(margin, margin, margin, margin); ++} ++//! [1] ++ ++//! [2] ++FlowLayout::~FlowLayout() ++{ ++ QLayoutItem *item; ++ while ((item = takeAt(0))) ++ delete item; ++} ++//! [2] ++ ++//! [3] ++void FlowLayout::addItem(QLayoutItem *item) ++{ ++ itemList.append(item); ++} ++//! [3] ++ ++//! [4] ++int FlowLayout::horizontalSpacing() const ++{ ++ if (m_hSpace >= 0) { ++ return m_hSpace; ++ } else { ++ return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); ++ } ++} ++ ++int FlowLayout::verticalSpacing() const ++{ ++ if (m_vSpace >= 0) { ++ return m_vSpace; ++ } else { ++ return smartSpacing(QStyle::PM_LayoutVerticalSpacing); ++ } ++} ++//! [4] ++ ++//! [5] ++int FlowLayout::count() const ++{ ++ return itemList.size(); ++} ++ ++QLayoutItem *FlowLayout::itemAt(int index) const ++{ ++ return itemList.value(index); ++} ++ ++QLayoutItem *FlowLayout::takeAt(int index) ++{ ++ if (index >= 0 && index < itemList.size()) ++ return itemList.takeAt(index); ++ else ++ return 0; ++} ++ ++int FlowLayout::getMaxinumHeight(int width) const ++{ ++ return doLayout(QRect(0, 0, width, 0), true); ++} ++//! [5] ++ ++//! [6] ++Qt::Orientations FlowLayout::expandingDirections() const ++{ ++ return 0; ++} ++//! [6] ++ ++//! [7] ++bool FlowLayout::hasHeightForWidth() const ++{ ++ return true; ++} ++ ++int FlowLayout::heightForWidth(int width) const ++{ ++ int height = doLayout(QRect(0, 0, width, 0), true); ++ return height; ++} ++//! [7] ++ ++//! [8] ++void FlowLayout::setGeometry(const QRect &rect) ++{ ++ QLayout::setGeometry(rect); ++ doLayout(rect, false); ++} ++ ++QSize FlowLayout::sizeHint() const ++{ ++ return minimumSize(); ++} ++ ++QSize FlowLayout::minimumSize() const ++{ ++ QSize size; ++ for (auto item : itemList) ++ size = size.expandedTo(item->minimumSize()); ++ ++ size += QSize(2*margin(), 2*margin()); ++ return size; ++} ++//! [8] ++ ++//! [9] ++int FlowLayout::doLayout(const QRect &rect, bool testOnly) const ++{ ++ int left, top, right, bottom; ++ getContentsMargins(&left, &top, &right, &bottom); ++ ++ QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); ++ //特殊处理,右侧留白 ++ effectiveRect.setWidth(effectiveRect.width() - 80); ++ int x = effectiveRect.x(); ++ int y = effectiveRect.y(); ++ int lineHeight = 0; ++//! [9] ++ ++//! [10] ++ for (auto item : itemList) { ++ QWidget *wid = item->widget(); ++ int spaceX = horizontalSpacing(); ++ if (spaceX == -1) ++ spaceX = wid->style()->layoutSpacing( ++ QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); ++ int spaceY = verticalSpacing(); ++ if (spaceY == -1) ++ spaceY = wid->style()->layoutSpacing( ++ QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); ++//! [10] ++//! [11] ++ int nextX = x + item->sizeHint().width() + spaceX; ++ if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) { ++ x = effectiveRect.x(); ++ y = y + lineHeight + spaceY; ++ nextX = x + item->sizeHint().width() + spaceX; ++ lineHeight = 0; ++ } ++ ++ if (!testOnly) ++ item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); ++ ++ x = nextX; ++ lineHeight = qMax(lineHeight, item->sizeHint().height()); ++ } ++ ++ return y + lineHeight - rect.y() + bottom; ++} ++//! [11] ++//! [12] ++int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const ++{ ++ QObject *parent = this->parent(); ++ if (!parent) { ++ return -1; ++ } else if (parent->isWidgetType()) { ++ QWidget *pw = static_cast<QWidget *>(parent); ++ return pw->style()->pixelMetric(pm, 0, pw); ++ } else { ++ return static_cast<QLayout *>(parent)->spacing(); ++ } ++} ++//! [12] +diff --git a/3rd-parties/layouts/flowlayout.h b/3rd-parties/layouts/flowlayout.h +new file mode 100644 +index 0000000..f46b782 +--- /dev/null ++++ b/3rd-parties/layouts/flowlayout.h +@@ -0,0 +1,89 @@ ++/**************************************************************************** ++** ++** Copyright (C) 2016 The Qt Company Ltd. ++** Contact: https://www.qt.io/licensing/ ++** ++** This file is part of the examples of the Qt Toolkit. ++** ++** $QT_BEGIN_LICENSE:BSD$ ++** Commercial License Usage ++** Licensees holding valid commercial Qt licenses may use this file in ++** accordance with the commercial license agreement provided with the ++** Software or, alternatively, in accordance with the terms contained in ++** a written agreement between you and The Qt Company. For licensing terms ++** and conditions see https://www.qt.io/terms-conditions. For further ++** information use the contact form at https://www.qt.io/contact-us. ++** ++** BSD License Usage ++** Alternatively, you may use this file under the terms of the BSD license ++** as follows: ++** ++** "Redistribution and use in source and binary forms, with or without ++** modification, are permitted provided that the following conditions are ++** met: ++** * Redistributions of source code must retain the above copyright ++** notice, this list of conditions and the following disclaimer. ++** * Redistributions in binary form must reproduce the above copyright ++** notice, this list of conditions and the following disclaimer in ++** the documentation and/or other materials provided with the ++** distribution. ++** * Neither the name of The Qt Company Ltd nor the names of its ++** contributors may be used to endorse or promote products derived ++** from this software without specific prior written permission. ++** ++** ++** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ++** ++** $QT_END_LICENSE$ ++** ++****************************************************************************/ ++ ++#ifndef FLOWLAYOUT_H ++#define FLOWLAYOUT_H ++ ++#include <QLayout> ++#include <QRect> ++#include <QStyle> ++//! [0] ++class FlowLayout : public QLayout ++{ ++public: ++ explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1); ++ explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1); ++ ~FlowLayout(); ++ ++ void addItem(QLayoutItem *item) override; ++ int horizontalSpacing() const; ++ int verticalSpacing() const; ++ Qt::Orientations expandingDirections() const override; ++ bool hasHeightForWidth() const override; ++ int heightForWidth(int) const override; ++ int count() const override; ++ QLayoutItem *itemAt(int index) const override; ++ QSize minimumSize() const override; ++ void setGeometry(const QRect &rect) override; ++ QSize sizeHint() const override; ++ QLayoutItem *takeAt(int index) override; ++ int getMaxinumHeight(int width) const; ++ ++private: ++ int doLayout(const QRect &rect, bool testOnly) const; ++ int smartSpacing(QStyle::PixelMetric pm) const; ++ ++ QList<QLayoutItem *> itemList; ++ int m_hSpace; ++ int m_vSpace; ++}; ++//! [0] ++ ++#endif // FLOWLAYOUT_H +diff --git a/3rd-parties/layouts/layouts.pri b/3rd-parties/layouts/layouts.pri +new file mode 100644 +index 0000000..e1b851e +--- /dev/null ++++ b/3rd-parties/layouts/layouts.pri +@@ -0,0 +1,5 @@ ++INCLUDEPATH += $$PWD ++ ++SOURCES += $$PWD/flowlayout.cpp ++ ++HEADERS += $$PWD/flowlayout.h +diff --git a/common.pri b/common.pri +index 98a770e..4fc25f8 100644 +--- a/common.pri ++++ b/common.pri +@@ -75,4 +75,11 @@ equals(USE_SET_ICON,1) { + message("USE_SET_ICON_GEOMETRY is not set") + } + ++BUILD_WITH_SDK_FEEDBACK_MENU_ACTION=$$(USE_FEEDBACK_MENU_ACTION) ++equals(BUILD_WITH_SDK_FEEDBACK_MENU_ACTION, 1) { ++ DEFINES += BUILD_WITH_FEEDBACK_ACTION ++} else { ++ message("do not build with kdk feedback menu action") ++} ++ + DEFINES += VFS_CUSTOM_PLUGIN +diff --git a/libpeony-qt/connect-to-server-dialog.cpp b/libpeony-qt/connect-to-server-dialog.cpp +index d4e6cb3..57d9ee7 100644 +--- a/libpeony-qt/connect-to-server-dialog.cpp ++++ b/libpeony-qt/connect-to-server-dialog.cpp +@@ -49,6 +49,8 @@ static const QString sambaTypeStr="samba"; + static const QString ftpDefaultPortStr="21"; + static const QString sftpDefaultPortStr="22"; + static const QString sambaDefaultPortStr="445"; ++static const QString Domain = "domain"; ++static const QString Password = "password"; + + static QString passwdEncode (QString p); + static QString passwdDecode (QString p); +@@ -265,7 +267,8 @@ ConnectServerDialog::ConnectServerDialog(QWidget *parent) : QDialog(parent) + m_port_editor->setEditText(sftpDefaultPortStr); + } + }); +- Q_EMIT m_remote_type_edit->currentTextChanged(ftpTypeStr); ++ m_remote_type_edit->setCurrentText(sftpTypeStr); ++ Q_EMIT m_remote_type_edit->currentTextChanged(sftpTypeStr); + + connect(m_btn_del, &QPushButton::clicked, this, [=] (bool checked) { + QString delUri = uri(); +@@ -472,6 +475,9 @@ void ConnectServerDialog::checkConnectIpAndPort(QString uri) + } + } + ++QMap<QString, QVariant> ConnectServerLogin::s_cacheUserInfo; ++QMap<QString, QVariant> ConnectServerLogin::s_tmpUserInfo; ++ + ConnectServerLogin::ConnectServerLogin(QString uri, QWidget *parent) + : QDialog(parent),m_remoteIP(uri) + { +@@ -511,8 +517,10 @@ ConnectServerLogin::ConnectServerLogin(QString uri, QWidget *parent) + + m_reg_usr_name_label = new QLabel; + m_reg_usr_passwd_label = new QLabel; ++ m_reg_usr_domain_label = new QLabel; + m_reg_usr_name_editor = new QComboBox; + m_reg_usr_passwd_editor = new QLineEdit; ++ m_reg_usr_domain_editor = new QLineEdit; + m_reg_usr_combox = new QCheckBox; + m_reg_usr_layout = new QGridLayout; + +@@ -520,18 +528,24 @@ ConnectServerLogin::ConnectServerLogin(QString uri, QWidget *parent) + m_reg_usr_name_label->setText(tr("Name")); + m_reg_usr_passwd_label->setText(tr("Password")); + m_reg_usr_combox->setText(tr("Remember the password")); ++ m_reg_usr_domain_label->setText(tr("domain")); ++ m_reg_usr_domain_editor->setText("WORKGROUP"); + m_reg_usr_name_label->setFixedHeight(36); + m_reg_usr_passwd_label->setFixedHeight(36); ++ m_reg_usr_domain_label->setFixedHeight(36); + m_reg_usr_name_editor->setFixedHeight(36); + m_reg_usr_passwd_editor->setFixedHeight(36); + m_reg_usr_combox->setFixedHeight(36); ++ m_reg_usr_domain_editor->setFixedHeight(36); + + m_reg_usr_passwd_editor->setEchoMode(QLineEdit::Password); + m_reg_usr_layout->addWidget(m_reg_usr_name_label, 0, 0); + m_reg_usr_layout->addWidget(m_reg_usr_name_editor, 0, 1); +- m_reg_usr_layout->addWidget(m_reg_usr_passwd_label, 1, 0); +- m_reg_usr_layout->addWidget(m_reg_usr_passwd_editor, 1, 1); +- m_reg_usr_layout->addWidget(m_reg_usr_combox, 2, 1); ++ m_reg_usr_layout->addWidget(m_reg_usr_domain_label, 1, 0); ++ m_reg_usr_layout->addWidget(m_reg_usr_domain_editor, 1, 1); ++ m_reg_usr_layout->addWidget(m_reg_usr_passwd_label, 2, 0); ++ m_reg_usr_layout->addWidget(m_reg_usr_passwd_editor, 2, 1); ++ m_reg_usr_layout->addWidget(m_reg_usr_combox, 3, 1); + m_reg_usr_layout->setVerticalSpacing(12); + m_main_layout->addLayout(m_reg_usr_layout); + +@@ -554,6 +568,7 @@ ConnectServerLogin::ConnectServerLogin(QString uri, QWidget *parent) + m_reg_usr_combox->setChecked (false); + + QMap<QString, QVariant> uriList = GlobalSettings::getInstance()->getValue(REMOTE_SERVER_REMOTE_IP).toMap(); ++ QMap<QString, QVariant> cacheUriList = ConnectServerLogin::getCacheUserInfo(); + QString portStr = QString::number(url.port()); + QString type = url.scheme(); + if (portStr.toInt() < 0) { +@@ -579,15 +594,37 @@ ConnectServerLogin::ConnectServerLogin(QString uri, QWidget *parent) + // set default passwd + QString du = m_reg_usr_name_editor->currentText (); + if (m_userInfo.contains (du)) { +- m_reg_usr_passwd_editor->setText (passwdDecode (m_userInfo[du].toByteArray ())); ++ m_reg_usr_domain_editor->setText(m_userInfo[du].toMap().value(Domain).toString()); ++ m_reg_usr_passwd_editor->setText(passwdDecode(m_userInfo[du].toMap().value(Password).toByteArray())); ++ //m_reg_usr_passwd_editor->setText (passwdDecode (m_userInfo[du].toByteArray ())); + m_reg_usr_combox->setChecked(true); + } ++ } else { ++ if (cacheUriList.contains(remoteUri)) { ++ QMap<QString, QVariant> cacheUserInfo = cacheUriList[remoteUri].toMap(); ++ if (!cacheUserInfo.isEmpty()) { ++ s_tmpUserInfo = cacheUserInfo; ++ for (auto u : cacheUserInfo.keys()) { ++ m_reg_usr_name_editor->addItem(u); ++ } ++ ++ QString currentText = m_reg_usr_name_editor->currentText(); ++ if (s_tmpUserInfo.contains(currentText)) { ++ //m_reg_usr_passwd_editor->setText(s_tmpUserInfo[currentText].toString()); ++ m_reg_usr_domain_editor->setText(s_tmpUserInfo[currentText].toMap().value(Domain).toString()); ++ m_reg_usr_passwd_editor->setText(s_tmpUserInfo[currentText].toMap().value(Password).toByteArray()); ++ } ++ } ++ } + } + } + ++ + connect (m_reg_usr_name_editor, &QComboBox::currentTextChanged, this, [=] (const QString& u) { +- if (m_userInfo.contains (u) && !m_userInfo[u].toString ().isEmpty ()) { +- m_reg_usr_passwd_editor->setText (passwdDecode (m_userInfo[u].toByteArray ())); ++ if (m_userInfo.contains (u) && /*!m_userInfo[u].toString ().isEmpty ()*/ !m_userInfo[u].toMap().isEmpty()) { ++ //m_reg_usr_passwd_editor->setText (passwdDecode (m_userInfo[u].toByteArray ())); ++ m_reg_usr_domain_editor->setText(m_userInfo[u].toMap().value(Domain).toString()); ++ m_reg_usr_passwd_editor->setText(passwdDecode(m_userInfo[u].toMap().value(Password).toByteArray())); + m_reg_usr_combox->setChecked(true); + } + }); +@@ -599,6 +636,8 @@ ConnectServerLogin::ConnectServerLogin(QString uri, QWidget *parent) + m_reg_usr_name_editor->setHidden(true); + m_reg_usr_passwd_label->setHidden(true); + m_reg_usr_passwd_editor->setHidden(true); ++ m_reg_usr_domain_label->setHidden(true); ++ m_reg_usr_domain_editor->setHidden(true); + }); + + connect(m_usr_btn_usr, &QRadioButton::clicked, [=] () { +@@ -608,6 +647,8 @@ ConnectServerLogin::ConnectServerLogin(QString uri, QWidget *parent) + m_reg_usr_name_editor->setHidden(false); + m_reg_usr_passwd_label->setHidden(false); + m_reg_usr_passwd_editor->setHidden(false); ++ m_reg_usr_domain_label->setHidden(false); ++ m_reg_usr_domain_editor->setHidden(false); + }); + + connect (m_reg_usr_combox, &QCheckBox::clicked, this, [=] (bool checked) { +@@ -640,7 +681,7 @@ QString ConnectServerLogin::user() + + QString ConnectServerLogin::domain() + { +- return "WORKGROUP"; ++ return m_reg_usr_domain_editor->text(); + } + + QString ConnectServerLogin::password() +@@ -690,7 +731,11 @@ void ConnectServerLogin::syncRemoteServer(const QUrl& url) + QMap<QString, QVariant> userInfo; + if (!uriList.contains (remoteUri)) { + if (savePassword () && !getPassWordProperty().isEmpty ()) { +- userInfo.insert (user(), passwdEncode (getPassWordProperty().toUtf8 ())); ++ QMap<QString, QVariant> tmpInfo; ++ tmpInfo.insert(Domain, domain()); ++ tmpInfo.insert(Password, passwdEncode(getPassWordProperty().toUtf8())); ++ userInfo.insert(user(), tmpInfo); ++ //userInfo.insert (user(), passwdEncode (getPassWordProperty().toUtf8 ())); + } + + uriList.insert (remoteUri, userInfo); +@@ -699,7 +744,11 @@ void ConnectServerLogin::syncRemoteServer(const QUrl& url) + userInfo = uriList[remoteUri].toMap (); + if (savePassword()){ + if (!getPassWordProperty().isEmpty ()) { +- userInfo[user()] = passwdEncode (getPassWordProperty().toUtf8 ()); ++ //userInfo[user()] = passwdEncode (getPassWordProperty().toUtf8 ()); ++ QMap<QString, QVariant> tmpInfo; ++ tmpInfo.insert(Domain, domain()); ++ tmpInfo.insert(Password, passwdEncode(getPassWordProperty().toUtf8())); ++ userInfo[user()] = tmpInfo; + } + }else { + if (userInfo.contains(m_reg_usr_name_editor->currentText ())) { +@@ -710,6 +759,8 @@ void ConnectServerLogin::syncRemoteServer(const QUrl& url) + uriList[remoteUri] = userInfo; + } + ++ updateCacheUserInfo(remoteUri); ++ + GlobalSettings::getInstance()->setValue(REMOTE_SERVER_REMOTE_IP,uriList); + GlobalSettings::getInstance()->forceSync(REMOTE_SERVER_REMOTE_IP); + } +@@ -720,6 +771,30 @@ QString ConnectServerLogin::getPassWordProperty() + return m_reg_usr_passwd_editor->property("password").toString(); + } + ++void ConnectServerLogin::setPassWordProperty(const QString &passwd) ++{ ++ m_reg_usr_passwd_editor->setProperty("password", passwd); ++} ++ ++void ConnectServerLogin::updateCacheUserInfo(const QString &remoteUri) ++{ ++ QMap<QString, QVariant> userInfo; ++ ++ if (!getPassWordProperty().isEmpty()) { ++ QMap<QString, QVariant> tmpInfo; ++ tmpInfo.insert(Domain, domain()); ++ tmpInfo.insert(Password, getPassWordProperty()); ++ userInfo.insert(user(), tmpInfo); ++ //userInfo.insert(user(), getPassWordProperty()); ++ } ++ ++ ConnectServerLogin::s_cacheUserInfo.insert(remoteUri, userInfo); ++} ++ ++QMap<QString, QVariant> ConnectServerLogin::getCacheUserInfo() ++{ ++ return s_cacheUserInfo; ++} + + static const unsigned char PEONY_AES_KEY[] = "peony key"; + +diff --git a/libpeony-qt/connect-to-server-dialog.h b/libpeony-qt/connect-to-server-dialog.h +index 1e01e86..25d7ed0 100644 +--- a/libpeony-qt/connect-to-server-dialog.h ++++ b/libpeony-qt/connect-to-server-dialog.h +@@ -108,6 +108,11 @@ public: + + void syncRemoteServer(const QUrl& url); + QString getPassWordProperty(); ++ void setPassWordProperty(const QString &passwd); ++ void updateCacheUserInfo(const QString &remoteUri); ++ ++ //add static function ++ static QMap<QString, QVariant> getCacheUserInfo(); + + private: + float m_widget_margin = 24; +@@ -124,8 +129,10 @@ private: + + QLabel* m_reg_usr_name_label = nullptr; + QLabel* m_reg_usr_passwd_label = nullptr; ++ QLabel* m_reg_usr_domain_label = nullptr; + QComboBox* m_reg_usr_name_editor = nullptr; + QLineEdit* m_reg_usr_passwd_editor = nullptr; ++ QLineEdit* m_reg_usr_domain_editor = nullptr; + QCheckBox* m_reg_usr_combox = nullptr; + QGridLayout* m_reg_usr_layout = nullptr; + +@@ -137,6 +144,8 @@ private: + + QMap<QString, QVariant> m_userInfo; + ++ static QMap<QString, QVariant> s_cacheUserInfo; ++ static QMap<QString, QVariant> s_tmpUserInfo; + }; + + class ButtonStyle : public QProxyStyle +diff --git a/libpeony-qt/controls/controls.pri b/libpeony-qt/controls/controls.pri +index 67bb028..e455f51 100644 +--- a/libpeony-qt/controls/controls.pri ++++ b/libpeony-qt/controls/controls.pri +@@ -12,9 +12,11 @@ include(tab-page/tab-page.pri) + + HEADERS += \ + $$PWD/icon-container.h \ ++ $$PWD/multi-select-combobox.h \ + $$PWD/tag-management.h + + SOURCES += \ + $$PWD/icon-container.cpp \ ++ $$PWD/multi-select-combobox.cpp \ + $$PWD/tag-management.cpp + +diff --git a/libpeony-qt/controls/directory-view/delegate/icon-view-delegate.cpp b/libpeony-qt/controls/directory-view/delegate/icon-view-delegate.cpp +index 02cf071..7fd8895 100644 +--- a/libpeony-qt/controls/directory-view/delegate/icon-view-delegate.cpp ++++ b/libpeony-qt/controls/directory-view/delegate/icon-view-delegate.cpp +@@ -335,11 +335,18 @@ void IconViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti + xoffset, + m_regFindKeyWords, + 2, +- 2); ++ 2, ++ info); + + painter->restore(); + + QList<int> emblemPoses = {4, 3, 2, 1}; //bottom right, bottom left, top right, top left ++ int emblemOffset = GlobalSettings::getInstance()->getValue(DEFAULT_VIEW_ZOOM_LEVEL).toInt() / 10; ++ int topLeftX = rect.x() + 10 + emblemOffset; ++ int topLeftY = rect.y() + 10 + emblemOffset; ++ int bottomRightX = rect.right() - 30 - emblemOffset; ++ int bottomRightY = opt.rect.y() + opt.decorationSize.height() - 10 - emblemOffset; ++ int emblemsSize = 20; + + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); +@@ -350,7 +357,7 @@ void IconViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti + //qDebug()<<info->symbolicIconName(); + //icon.paint(painter, rect.x() + rect.width() - 30, rect.y() + 10, 20, 20, Qt::AlignCenter); + //Adjust link emblem to topLeft.link story#8354 +- icon.paint(painter, rect.x() + 10, opt.rect.y() + opt.decorationSize.height() - 10, 20, 20, Qt::AlignCenter); ++ icon.paint(painter, topLeftX, bottomRightY, emblemsSize, emblemsSize, Qt::AlignCenter); + } + + if(view->isEnableMultiSelect()) { +@@ -365,16 +372,25 @@ void IconViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti + + //paint access emblems + //NOTE: we can not query the file attribute in smb:///(samba) and network:///. +- if (info->uri().startsWith("file:")) { +- if (!info->canRead()) { ++ if (info->uri().startsWith("file:") || info->uri().startsWith("mtp:") || info->uri().startsWith("gphoto2:") || info->uri().startsWith("filesafe:")) { ++ /** ++ * @bug #262561: [File Manager] PDF desktop shortcut files with deleted source files ++ * do not display the same icon on the desktop folder as on the desktop. ++ * ++ * If the source file of a symbolic link is deleted, an “X” icon will be displayed in the upper left corner. ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-09-11 ++ */ ++ if (!info->canRead() || !info->isExistTargetOfSymlink()) { + emblemPoses.removeOne(1); + QIcon icon = QIcon::fromTheme("emblem-unreadable"); +- icon.paint(painter, rect.x() + 10, rect.y() + 10, 20, 20); ++ icon.paint(painter, topLeftX, topLeftY, emblemsSize, emblemsSize); + } else if (!info->canWrite()/* && !info->canExecute()*/) { + //只读图标对应可读不可写情况,与可执行权限无关,link to bug#99998 + emblemPoses.removeOne(1); + QIcon icon = QIcon::fromTheme("emblem-readonly"); +- icon.paint(painter, rect.x() + 10, rect.y() + 10, 20, 20); ++ icon.paint(painter, topLeftX, topLeftY, emblemsSize, emblemsSize); + } + } + +@@ -391,19 +407,19 @@ void IconViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti + int pos = emblemPoses.takeFirst(); + switch (pos) { + case 1: { +- icon.paint(painter, rect.x() + 10, rect.y() + 10, 20, 20, Qt::AlignCenter); ++ icon.paint(painter, topLeftX, topLeftY, emblemsSize, emblemsSize, Qt::AlignCenter); + break; + } + case 2: { +- icon.paint(painter, rect.x() + rect.width() - 30, rect.y() + 10, 20, 20, Qt::AlignCenter); ++ icon.paint(painter, bottomRightX, topLeftY, emblemsSize, emblemsSize, Qt::AlignCenter); + break; + } + case 3: { +- icon.paint(painter, rect.x() + 10, opt.rect.y() + opt.decorationSize.height() - 10, 20, 20, Qt::AlignCenter); ++ icon.paint(painter, topLeftX, bottomRightY, emblemsSize, emblemsSize, Qt::AlignCenter); + break; + } + case 4: { +- icon.paint(painter, rect.right() - 30, opt.rect.y() + opt.decorationSize.height() - 10, 20, 20, Qt::AlignCenter); ++ icon.paint(painter, bottomRightX, bottomRightY, emblemsSize, emblemsSize, Qt::AlignCenter); + break; + } + default: +@@ -448,6 +464,10 @@ QWidget *IconViewDelegate::createEditor(QWidget *parent, const QStyleOptionViewI + } + if (fsType.contains("ext")) { + edit->setMaxLengthLimit(255 - suffix.toLocal8Bit().length()); ++ } else if (fsType == "ecryptfs") { ++ edit->setMaxLengthLimit(143 - suffix.toLocal8Bit().length()); ++ } else if (fsType == "udf") { ++ edit->setMaxLengthLimit(254 - suffix.toLocal8Bit().length()); + } else if (fsType.contains("ntfs")) { + edit->setLimitBytes(false); + edit->setMaxLengthLimit(255 - suffix.length()); +@@ -563,6 +583,10 @@ void IconViewDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + auto infoJob = new Peony::FileInfoJob(Peony::FileInfo::fromUri(uri)); + infoJob->setAutoDelete(); + connect(infoJob, &Peony::FileInfoJob::queryAsyncFinished, this, [=]() { ++ /* hotfix bug#225573 【文件管理器】多次进行重命名文档中的文件操作后,文档中文件选中、重命名异常;modified on 2024-08-01 */ ++ if(index != getView()->m_last_index && getView()->m_last_index.isValid() ){ ++ return; ++ }//end + getView()->setSelections(QStringList()<<uri); + getView()->scrollToSelection(uri); + //set focus to fix bug#54061 +@@ -583,6 +607,10 @@ void IconViewDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + auto infoJob = new Peony::FileInfoJob(Peony::FileInfo::fromUri(uri)); + infoJob->setAutoDelete(); + connect(infoJob, &Peony::FileInfoJob::queryAsyncFinished, this, [=]() { ++ /* hotfix bug#225573 【文件管理器】多次进行重命名文档中的文件操作后,文档中文件选中、重命名异常;modified on 2024-08-01 */ ++ if(index != getView()->m_last_index && getView()->m_last_index.isValid() ){ ++ return; ++ }//end + getView()->setSelections(QStringList()<<uri); + getView()->scrollToSelection(uri); + //set focus to fix bug#54061 +@@ -652,7 +680,7 @@ const QString IconViewDelegate::getRegFindKeyWords() const + return m_regFindKeyWords; + } + +-void IconViewTextHelper::paintText(QPainter *painter, const QStyleOptionViewItem &option, int textMaxHeight, int xOffset, const QString ®FindKeyWords, int horizalMargin, int maxLineCount) ++void IconViewTextHelper::paintText(QPainter *painter, const QStyleOptionViewItem &option, int textMaxHeight, int xOffset, const QString ®FindKeyWords, int horizalMargin, int maxLineCount, std::shared_ptr<FileInfo> info) + { + painter->save(); + QFont font = option.font; +@@ -709,6 +737,13 @@ void IconViewTextHelper::paintText(QPainter *painter, const QStyleOptionViewItem + } + document.setPlainText(elidedText); + ++ if (isElided && info != nullptr) { ++ if (option.text != elidedText) { ++ info->setProperty("isElided", true); ++ } else { ++ info->setProperty("isElided", false); ++ } ++ } + painter->translate(horizalMargin, 0); + + //设置关键字高亮 +@@ -916,7 +951,7 @@ QSize IconViewTextHelper::getTextSizeForIndex(const QStyleOptionViewItem &option + return QSize(fixedWidth, textHight); + } + +-void IconViewTextHelper::paintText(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index, int textMaxHeight, int horizalMargin, int maxLineCount, bool useSystemPalette, const QColor &customColor) ++void IconViewTextHelper::paintText(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index, int textMaxHeight, int horizalMargin, int maxLineCount, bool useSystemPalette, const QColor &customColor, std::shared_ptr<FileInfo> info) + { + painter->save(); + painter->translate(horizalMargin, 0); +@@ -951,6 +986,8 @@ void IconViewTextHelper::paintText(QPainter *painter, const QStyleOptionViewItem + int width = option.rect.width() - 2*horizalMargin; + + int y = 0; ++ bool isElided= false; ++ QString elidedText = option.text; + while (true) { + QTextLine line = textLayout.createLine(); + if (!line.isValid()) +@@ -966,6 +1003,10 @@ void IconViewTextHelper::paintText(QPainter *painter, const QStyleOptionViewItem + } else { + QString lastLine = option.text.mid(line.textStart()); + QString elidedLastLine = fontMetrics.elidedText(lastLine, Qt::ElideRight, width); ++ if (elidedLastLine != lastLine) { ++ isElided = true; ++ elidedText = elidedLastLine; ++ } + auto rect = QRect(horizalMargin, y /*+ fontMetrics.ascent()*/, width, textMaxHeight); + //opt.setWrapMode(QTextOption::NoWrap); + opt.setWrapMode(QTextOption::NoWrap); +@@ -977,5 +1018,12 @@ void IconViewTextHelper::paintText(QPainter *painter, const QStyleOptionViewItem + } + textLayout.endLayout(); + ++ if (isElided && info != nullptr) { ++ if (option.text != elidedText) { ++ info->setProperty("isElided", true); ++ } else { ++ info->setProperty("isElided", false); ++ } ++ } + painter->restore(); + } +diff --git a/libpeony-qt/controls/directory-view/delegate/icon-view-delegate.h b/libpeony-qt/controls/directory-view/delegate/icon-view-delegate.h +index 8e191ac..b2382f5 100644 +--- a/libpeony-qt/controls/directory-view/delegate/icon-view-delegate.h ++++ b/libpeony-qt/controls/directory-view/delegate/icon-view-delegate.h +@@ -26,6 +26,7 @@ + #include <QStyledItemDelegate> + #include <peony-core_global.h> + #include <QFileSystemWatcher> ++#include <memory> + + class QPushButton; + +@@ -33,6 +34,7 @@ namespace Peony { + + class DesktopIconViewDelegate; + class DesktopIndexWidget; ++class FileInfo; + + namespace DirectoryView { + +@@ -116,7 +118,8 @@ class PEONYCORESHARED_EXPORT IconViewTextHelper + const QModelIndex &index, + int textMaxHeight, + int horizalMargin = 0, +- int maxLineCount = 4, bool useSystemPalette = true, const QColor &customColor = Qt::transparent); ++ int maxLineCount = 4, bool useSystemPalette = true, const QColor &customColor = Qt::transparent, ++ std::shared_ptr<FileInfo> info = nullptr); + + static void paintText(QPainter *painter, + const QStyleOptionViewItem &option, +@@ -124,7 +127,8 @@ class PEONYCORESHARED_EXPORT IconViewTextHelper + int xOffset, + const QString ®FindKeyWords, + int horizalMargin = 0, +- int maxLineCount = 4) ; ++ int maxLineCount = 4, ++ std::shared_ptr<FileInfo> info = nullptr); + + static qreal drawText(QPainter *painter, + const QStyleOptionViewItem &option, +@@ -133,6 +137,7 @@ class PEONYCORESHARED_EXPORT IconViewTextHelper + const QString ®FindKeyWords, + int horizalMargin = 0, + int maxLineCount = 4) ; ++ + }; + + } +diff --git a/libpeony-qt/controls/directory-view/delegate/icon-view-index-widget.cpp b/libpeony-qt/controls/directory-view/delegate/icon-view-index-widget.cpp +index eb9ce06..4e4c2b2 100644 +--- a/libpeony-qt/controls/directory-view/delegate/icon-view-index-widget.cpp ++++ b/libpeony-qt/controls/directory-view/delegate/icon-view-index-widget.cpp +@@ -41,6 +41,7 @@ + #include "file-item.h" + #include "file-utils.h" + #include "emblem-provider.h" ++#include "global-settings.h" + + #include <QDebug> + #include <QTextLayout> +@@ -202,14 +203,25 @@ void IconViewIndexWidget::paintEvent(QPaintEvent *e) + + QWidget::paintEvent(e); + QPainter p(this); +-// p.fillRect(0, 0, 999, 999, qApp->palette().base()); + +- //adjustPos(); ++ auto opt = m_option; ++ auto rawRect = m_option.rect; ++ opt.rect = this->rect(); ++ ++ int horizalMargin = 2; ++ auto fontMetrics = opt.fontMetrics; ++ int pixelsWide = fontMetrics.width(opt.text); ++ int width = opt.rect.width() - 2*horizalMargin; ++ ++ if(pixelsWide < width){ ++ opt.rect = opt.rect.adjusted(0,0,0,-31); ++ } ++ + auto bgColor = QApplication::palette().base().color(); + p.save(); + p.setPen(Qt::transparent); + p.setBrush(bgColor); +- p.drawRoundedRect(this->rect(), 6, 6); ++ p.drawRoundedRect(opt.rect, 6, 6); + p.restore(); + //qDebug()<<m_option.backgroundBrush; + //qDebug()<<this->size() << m_delegate->getView()->iconSize(); +@@ -228,19 +240,6 @@ void IconViewIndexWidget::paintEvent(QPaintEvent *e) + }//end + #endif + +- auto opt = m_option; +- auto rawRect = m_option.rect; +- opt.rect = this->rect(); +- +- int horizalMargin = 2; +- auto fontMetrics = opt.fontMetrics; +- int pixelsWide = fontMetrics.width(opt.text); +- int width = opt.rect.width() - 2*horizalMargin; +- +- if(pixelsWide < width){ +- opt.rect = opt.rect.adjusted(0,0,0,-31); +- } +- + opt.palette = QApplication::palette(); + //p.fillRect(opt.rect, m_delegate->selectedBrush()); + auto rawDecoSize = opt.decorationSize; +@@ -279,6 +278,9 @@ void IconViewIndexWidget::paintEvent(QPaintEvent *e) + if(info->uri().startsWith("favorite://")){/* 快速访问须特殊处理 */ + info = FileInfo::fromUri(FileUtils::getEncodedUri(FileUtils::getTargetUri(info->uri()))); + } ++ if (info->uri().startsWith("filesafe:///")) { ++ opt.icon = qvariant_cast<QIcon>(m_index.data(Qt::DecorationRole)); ++ } + auto colors = info->getColors(); + auto lineSpacing = opt.fontMetrics.lineSpacing(); + int yoffset = 0; +@@ -360,6 +362,12 @@ void IconViewIndexWidget::paintEvent(QPaintEvent *e) + p.restore(); + + QList<int> emblemPoses = {4, 3, 2, 1}; //bottom right, bottom left, top right, top left ++ int emblemOffset = GlobalSettings::getInstance()->getValue(DEFAULT_VIEW_ZOOM_LEVEL).toInt() / 10; ++ int topLeftX = rect().x() + 10 + emblemOffset; ++ int topLeftY = rect().y() + 10 + emblemOffset; ++ int bottomRightX = rect().right() - 30 - emblemOffset; ++ int bottomRightY = m_delegate->getView()->iconSize().height() - 10 - emblemOffset; ++ int emblemsSize = 20; + + //paint symbolic link emblems + if (info->isSymbolLink()) { +@@ -370,7 +378,7 @@ void IconViewIndexWidget::paintEvent(QPaintEvent *e) + //Adjust link emblem to topLeft.link story#8354 + p.save(); + p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); +- icon.paint(&p, this->rect().x() + 10, m_delegate->getView()->iconSize().height() - 10, 20, 20, Qt::AlignCenter); ++ icon.paint(&p, topLeftX, bottomRightY, emblemsSize, emblemsSize, Qt::AlignCenter); + p.restore(); + } + if(view->isEnableMultiSelect()) +@@ -381,17 +389,25 @@ void IconViewIndexWidget::paintEvent(QPaintEvent *e) + + //paint access emblems + //NOTE: we can not query the file attribute in smb:///(samba) and network:///. +- if (!info->uri().startsWith("file:")) { ++ if (!info->uri().startsWith("file:") && !info->uri().startsWith("filesafe:")) { + return; + } + +- auto rect = this->rect(); +- if (!info->canRead()) { ++ /** ++ * @bug #262561: [File Manager] PDF desktop shortcut files with deleted source files ++ * do not display the same icon on the desktop folder as on the desktop. ++ * ++ * If the source file of a symbolic link is deleted, an “X” icon will be displayed in the upper left corner. ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-09-11 ++ */ ++ if (!info->canRead() || !info->isExistTargetOfSymlink()) { + emblemPoses.removeOne(1); + QIcon icon = QIcon::fromTheme("emblem-unreadable"); + p.save(); + p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); +- icon.paint(&p, rect.x() + 10, rect.y() + 10, 20, 20); ++ icon.paint(&p, topLeftX, topLeftY, 20, 20); + p.restore(); + } else if (!info->canWrite()/* && !info->canExecute()*/) { + //只读图标对应可读不可写情况,与可执行权限无关,link to bug#99998 +@@ -399,47 +415,46 @@ void IconViewIndexWidget::paintEvent(QPaintEvent *e) + QIcon icon = QIcon::fromTheme("emblem-readonly"); + p.save(); + p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); +- icon.paint(&p, rect.x() + 10, rect.y() + 10, 20, 20); ++ icon.paint(&p, topLeftX, topLeftY, emblemsSize, emblemsSize); + p.restore(); + } + + // paint extension emblems, FIXME: adjust layout, and implemet on indexwidget, other view. +- auto extensionsEmblems = EmblemProviderManager::getInstance()->getAllEmblemsForUri(info->uri()); ++ auto extensionsEmblems = EmblemProviderManager::getInstance()->getAllEmblemsForUri(info->uri()); + +- for (auto extensionsEmblem : extensionsEmblems) { +- if (emblemPoses.isEmpty()) { ++ for (auto extensionsEmblem : extensionsEmblems) { ++ if (emblemPoses.isEmpty()) { ++ break; ++ } ++ ++ QIcon icon = QIcon::fromTheme(extensionsEmblem); ++ if (!icon.isNull()) { ++ p.save(); ++ p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); ++ int pos = emblemPoses.takeFirst(); ++ switch (pos) { ++ case 1: { ++ icon.paint(&p, topLeftX, topLeftY, emblemsSize, emblemsSize, Qt::AlignCenter); + break; + } +- +- QIcon icon = QIcon::fromTheme(extensionsEmblem); +- if (!icon.isNull()) { +- p.save(); +- p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); +- int pos = emblemPoses.takeFirst(); +- switch (pos) { +- case 1: { +- icon.paint(&p, rect.x() + 10, rect.y() + 10, 20, 20, Qt::AlignCenter); +- break; +- } +- case 2: { +- icon.paint(&p, rect.x() + rect.width() - 30, rect.y() + 10, 20, 20, Qt::AlignCenter); +- break; +- } +- case 3: { +- icon.paint(&p, rect.x() + 10, m_delegate->getView()->iconSize().height() - 10, 20, 20, Qt::AlignCenter); +- break; +- } +- case 4: { +- icon.paint(&p, rect.right() - 30, m_delegate->getView()->iconSize().height() - 10, 20, 20, Qt::AlignCenter); +- break; +- } +- default: +- break; +- } +- p.restore(); ++ case 2: { ++ icon.paint(&p, bottomRightX, topLeftX, emblemsSize, emblemsSize, Qt::AlignCenter); ++ break; + } ++ case 3: { ++ icon.paint(&p, topLeftX, bottomRightY, emblemsSize, emblemsSize, Qt::AlignCenter); ++ break; ++ } ++ case 4: { ++ icon.paint(&p, bottomRightX, bottomRightY, emblemsSize, emblemsSize, Qt::AlignCenter); ++ break; ++ } ++ default: ++ break; ++ } ++ p.restore(); + } +- ++ } + } + + void IconViewIndexWidget::mousePressEvent(QMouseEvent *e) +@@ -497,7 +512,24 @@ void IconViewIndexWidget::mousePressEvent(QMouseEvent *e) + view->m_editValid = false; + view->m_renameTimer->start(); + } +- e->ignore(); ++ //e->accept(); ++ //return; ++ /** ++ * @bug #239593: [File Manager] Click on any folder to enter and then return. After returning, cannot drag the folder directly ++ * ++ * Transfer the mousePressEvent event of IconViewIndexWidget to the IconView class for further processing ++ * Used to handle drag and drop events ++ * ++ * @author Renyg ++ * @date 2024-07-11 ++ */ ++ return QWidget::mousePressEvent(e); ++// if (m_edit_trigger.isActive()) { ++// qDebug()<<"IconViewIndexWidget::mousePressEvent: edit"<<e->type(); ++// m_delegate->getView()->setIndexWidget(m_index, nullptr); ++// m_delegate->getView()->edit(m_index); ++// return; ++// } + } + if(e->button() == Qt::RightButton){ + e->accept(); +@@ -521,6 +553,18 @@ void IconViewIndexWidget::mouseReleaseEvent(QMouseEvent *e) + + void IconViewIndexWidget::mouseDoubleClickEvent(QMouseEvent *event) + { ++ /** ++ * @bug #250731: [File Manager] Right clicking on the same folder several times in the file manager will take you to the folder ++ * ++ * Prevent double-click events from triggering on right-click ++ * Only double left clicks will be processed ++ * ++ * @author Renyg ++ * @date 2024-08-12 ++ */ ++ if (event->button() == Qt::RightButton) { ++ return; ++ } + bool singleClicked = qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick); + bool isPreviewMode = m_delegate->getView()->topLevelWidget()->property("isPreviewMode").toBool(); + if (!singleClicked || isPreviewMode) { +diff --git a/libpeony-qt/controls/directory-view/delegate/list-view-delegate.cpp b/libpeony-qt/controls/directory-view/delegate/list-view-delegate.cpp +index 3b62985..9f6a315 100644 +--- a/libpeony-qt/controls/directory-view/delegate/list-view-delegate.cpp ++++ b/libpeony-qt/controls/directory-view/delegate/list-view-delegate.cpp +@@ -34,7 +34,10 @@ + #include "file-info.h" + #include "file-info-job.h" + #include "emblem-provider.h" ++#include "global-settings.h" ++#include "list-view-style.h" + ++#include <memory> + #include <QTimer> + #include <QPushButton> + +@@ -135,6 +138,7 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti + opt.text = text1; + painter->save(); + ++ setElidedNamePolicy(info, opt); + QString text = opt.text; + QFont font = opt.font; + QFontMetrics fontMetrics = opt.fontMetrics; +@@ -185,6 +189,7 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti + document.drawContents(painter, textRect); + painter->restore(); + } else { ++ setElidedNamePolicy(info, opt); + opt.widget->style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget); + } + if(view->isEnableMultiSelect()) { +@@ -211,13 +216,16 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti + + //add link and read only icon support + if (index.column() == 0) { ++ int emblemOffset = 4 - GlobalSettings::getInstance()->getValue(DEFAULT_VIEW_ZOOM_LEVEL).toInt() / 5; ++ int bottomOff = 6 + GlobalSettings::getInstance()->getValue(DEFAULT_VIEW_ZOOM_LEVEL).toInt() / 2; + auto rect = view->visualRect(index); + auto iconSize = view->iconSize(); + auto size = iconSize.width()/2; + bool isSymbolicLink = info->isSymbolLink(); +- auto loc_x = rect.x() + iconSize.width() - size/2; ++ auto loc_x = rect.x() + emblemOffset; + auto loc_y = rect.y(); +- auto iconSizeHeight = iconSize.height(); ++ auto bottomY = rect.y() + rect.height() - (rect.height() - iconSize.height()) / 2; ++ + //paint symbolic link emblems + if (isSymbolicLink) { + emblemPoses.removeOne(3); +@@ -225,22 +233,25 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti + //qDebug()<<info->symbolicIconName(); + //icon.paint(painter, loc_x, loc_y, size, size); + //Adjust link emblem to topLeft.link story#8354 +- loc_x = rect.x(); +- //Special calculation emblems coordinates +- if(iconSize.height() < 28){ +- iconSizeHeight = 28; +- } + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); +- icon.paint(painter, loc_x, loc_y + iconSizeHeight - size/2 - 5, size, size, Qt::AlignCenter); ++ icon.paint(painter, loc_x, bottomY - bottomOff, size, size, Qt::AlignCenter); + painter->restore(); + } + + //paint access emblems + //NOTE: we can not query the file attribute in smb:///(samba) and network:///. +- loc_x = rect.x(); +- if (info->uri().startsWith("file:")) { +- if (!info->canRead()) { ++ if (info->uri().startsWith("file:") || info->uri().startsWith("mtp:") || info->uri().startsWith("gphoto2:") || info->uri().startsWith("filesafe:")) { ++ /** ++ * @bug #262561: [File Manager] PDF desktop shortcut files with deleted source files ++ * do not display the same icon on the desktop folder as on the desktop. ++ * ++ * If the source file of a symbolic link is deleted, an “X” icon will be displayed in the upper left corner. ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-09-11 ++ */ ++ if (!info->canRead() || !info->isExistTargetOfSymlink()) { + emblemPoses.removeOne(1); + QIcon icon = QIcon::fromTheme("emblem-unreadable"); + painter->save(); +@@ -258,17 +269,12 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti + } + } + +- // paint extension emblems, FIXME: adjust layout, and implemet on indexwidget, other view. ++ // paint extension emblems, FIXME: adjust layout, and implemet on indexwidget, other view. + auto extensionsEmblems = EmblemProviderManager::getInstance()->getAllEmblemsForUri(info->uri()); + +- //Special calculation emblems coordinates +- if(iconSize.height() < 28){ +- iconSizeHeight = 28; +- } +- + for (auto extensionsEmblem : extensionsEmblems) { + if (emblemPoses.isEmpty()) { +- break; ++ break; + } + + QIcon icon = QIcon::fromTheme(extensionsEmblem); +@@ -278,29 +284,28 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti + int pos = emblemPoses.takeFirst(); + switch (pos) { + case 1: { +- icon.paint(painter, loc_x, loc_y, size, size, Qt::AlignCenter); +- break; ++ icon.paint(painter, loc_x, loc_y + emblemOffset, size, size, Qt::AlignCenter); ++ break; + } + case 2: { +- icon.paint(painter, loc_x + iconSize.width() - size/2, loc_y, size, size, Qt::AlignCenter); +- break; ++ icon.paint(painter, loc_x + iconSize.width() - size/2, loc_y + emblemOffset, size, size, Qt::AlignCenter); ++ break; + } + case 3: { +- icon.paint(painter, loc_x, loc_y + iconSizeHeight - size/2 - 5, size, size, Qt::AlignCenter); +- break; ++ icon.paint(painter, loc_x, bottomY - bottomOff, size, size, Qt::AlignCenter); ++ break; + } + case 4: { +- icon.paint(painter, loc_x + iconSize.width() - size/2, loc_y + iconSizeHeight - size/2 - 5, size, size, Qt::AlignCenter); +- break; ++ icon.paint(painter, loc_x + iconSize.width() - size/2, bottomY - bottomOff, size, size, Qt::AlignCenter); ++ break; + } + default: +- break; ++ break; + } + painter->restore(); + } + } + } +- + } + + QWidget *ListViewDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const +@@ -329,6 +334,12 @@ QWidget *ListViewDelegate::createEditor(QWidget *parent, const QStyleOptionViewI + if (fsType.contains("ext")) { + maxLength = 255 - suffix.toLocal8Bit().length(); + edit->setMaxLengthLimit(maxLength); ++ } else if (fsType == "ecryptfs") { ++ maxLength = 143 - suffix.toLocal8Bit().length(); ++ edit->setMaxLengthLimit(maxLength); ++ } else if (fsType == "udf") { ++ maxLength = 254 - suffix.toLocal8Bit().length(); ++ edit->setMaxLengthLimit(maxLength); + } else if (fsType.contains("ntfs")) { + edit->setLimitBytes(false); + maxLength = 255 - suffix.length(); +@@ -357,10 +368,8 @@ QWidget *ListViewDelegate::createEditor(QWidget *parent, const QStyleOptionViewI + auto text = edit->toPlainText(); + //fix bug#220283, rename edit position wrong issue + //short file name no need update to avoid position wrong +- if (text.length() >= maxLength) { +- edit->adjustText(); +- updateEditorGeometry(edit, option, index); +- } ++ edit->adjustText(); ++ updateEditorGeometry(edit, option, index); + }); + + connect(edit, &TextEdit::finishEditRequest, this, &ListViewDelegate::slot_finishEdit); +@@ -400,13 +409,17 @@ void ListViewDelegate::setEditorData(QWidget *editor, const QModelIndex &index) + edit->setTextCursor(cursor); + } + +-//void ListViewDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const +-//{ +-// QStyledItemDelegate::updateEditorGeometry(editor, option, index); +-// TextEdit *edit = qobject_cast<TextEdit*>(editor); +-// edit->setFixedHeight(editor->height()); +-// edit->resize(edit->document()->size().width(), -1); +-//} ++void ListViewDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const ++{ ++ QStyledItemDelegate::updateEditorGeometry(editor, option, index); ++ TextEdit *edit = qobject_cast<TextEdit*>(editor); ++ edit->m_backgroundEdit->setGeometry(1, 2, edit->size().width() - 2, edit->size().height() - 4); ++ QTimer::singleShot(0, edit, [=]() { ++ int top = (edit->height() - edit->document()->size().height())/2; ++ top = top < 2 ? 2 : top; ++ edit->setMargins(1, top, 1, 2); ++ }); ++} + + void ListViewDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const + { +@@ -592,6 +605,21 @@ void ListViewDelegate::paintLabel(QStyleOptionViewItem &opt, int aalignment, QLi + } + } + ++void ListViewDelegate::setElidedNamePolicy(const std::shared_ptr<FileInfo>& info, const QStyleOptionViewItem &opt) ++{ ++ // Early return if the display name doesn't match the text in the style option ++ if (info->displayName() != opt.text) { ++ return; ++ } ++ ++ const QFontMetrics fm(opt.font); ++ const QRect textRect = opt.widget->style()->subElementRect(QStyle::SE_ItemViewItemText, &opt, opt.widget); ++ const QString elidedText = fm.elidedText(info->displayName(), Qt::ElideRight, textRect.width()); ++ ++ // Set the 'isElided' property based on whether the text is truncated ++ info->setProperty("isElided", elidedText != info->displayName()); ++} ++ + //TextEdit + TextEdit::TextEdit(QWidget *parent) : QTextEdit (parent) + { +@@ -600,6 +628,14 @@ TextEdit::TextEdit(QWidget *parent) : QTextEdit (parent) + setFrameShape(QFrame::NoFrame); + setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + setViewportMargins(1, 2, 1, 2); ++ if (m_backgroundEdit == nullptr) { ++ m_backgroundEdit = new QTextEdit(this); ++ m_backgroundEdit->setReadOnly(true); ++ m_backgroundEdit->setFrameShape(QTextEdit::NoFrame); ++ ++ m_backgroundEdit->setGeometry(1, 2, size().width() - 2, size().height() - 4); ++ m_backgroundEdit->lower(); ++ } + } + + void TextEdit::adjustText() +@@ -651,3 +687,8 @@ void TextEdit::keyPressEvent(QKeyEvent *e) + } + return QTextEdit::keyPressEvent(e); + } ++ ++void TextEdit::setMargins(int left, int top, int right, int bottom) ++{ ++ setViewportMargins(left, top, right, bottom); ++} +diff --git a/libpeony-qt/controls/directory-view/delegate/list-view-delegate.h b/libpeony-qt/controls/directory-view/delegate/list-view-delegate.h +index 19190e0..a379f1f 100644 +--- a/libpeony-qt/controls/directory-view/delegate/list-view-delegate.h ++++ b/libpeony-qt/controls/directory-view/delegate/list-view-delegate.h +@@ -23,6 +23,7 @@ + #ifndef LISTVIEWDELEGATE_H + #define LISTVIEWDELEGATE_H + ++#include <memory> + #include <QStyledItemDelegate> + #include <QTextEdit> + #include "peony-core_global.h" +@@ -33,6 +34,7 @@ class QPushButton; + namespace Peony { + + class TextEdit; ++class FileInfo; + + class ListViewDelegate : public QStyledItemDelegate + { +@@ -51,7 +53,7 @@ public: + //edit + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + void setEditorData(QWidget *editor, const QModelIndex &index) const override; +- //void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override; ++ void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; + int getCurrentCheckboxColumn(){ + return m_checkbox_column; +@@ -60,6 +62,18 @@ public: + void setSearchKeyword(QString regFindKeyWords); + void paintLabel(QStyleOptionViewItem &opt, int aalignment, QList<QColor> colors, QPainter *painter) const; + ++ /** ++ * @brief Sets the elided name policy for a file item. ++ * ++ * This function determines whether the display name of a file needs to be elided ++ * based on the available space in the view. It sets a property on the FileInfo ++ * object to indicate whether the name is elided or not. ++ * ++ * @param info Shared pointer to the FileInfo object. ++ * @param opt The style option containing display information. ++ */ ++ static void setElidedNamePolicy(const std::shared_ptr<FileInfo>& info, const QStyleOptionViewItem &opt); ++ + Q_SIGNALS: + void isEditing(bool editing) const; + void requestDone(QWidget *editor); +@@ -82,6 +96,9 @@ public: + void setMaxLengthLimit(int length); + void setLimitBytes(bool limitBytes); + ++ QTextEdit *m_backgroundEdit = nullptr; ++ void setMargins(int left, int top, int right, int bottom); ++ + Q_SIGNALS: + void finishEditRequest(); + +diff --git a/libpeony-qt/controls/directory-view/directory-view-container.cpp b/libpeony-qt/controls/directory-view/directory-view-container.cpp +index 0ba37e7..7408d5c 100644 +--- a/libpeony-qt/controls/directory-view/directory-view-container.cpp ++++ b/libpeony-qt/controls/directory-view/directory-view-container.cpp +@@ -40,6 +40,7 @@ + + #include <QVBoxLayout> + #include <QAction> ++#include <QTimer> + + #include <QApplication> + +@@ -83,15 +84,28 @@ DirectoryViewContainer::DirectoryViewContainer(QWidget *parent) : QWidget(parent + } + }); + ++ connect(m_model, &FileItemModel::signal_updateTabPageTitle, [=](const QString& uri){ ++ Q_EMIT this->signal_updateTabPageTitle(uri); ++ }); ++ connect(m_model, &FileItemModel::signal_updateLocationBar, [=](const QString& uri){ ++ Q_EMIT this->signal_updateLocationBar(uri); ++ }); ++ + // connect(FileLabelModel::getGlobalModel(), &FileLabelModel::dataChanged, this, [=](){ + // refresh(); + // }); ++ connect(m_model, &FileItemModel::updateFilter, [=](){ ++ updateFilter(); ++ QTimer::singleShot(400, this, [=](){ ++ Q_EMIT this->statusBarChanged(); ++ }); ++ }); + + if (QGSettings::isSchemaInstalled("org.ukui.control-center.panel.plugins")) { + m_control_center_plugin = new QGSettings("org.ukui.control-center.panel.plugins", QByteArray(), this); + connect(m_control_center_plugin, &QGSettings::changed, this, [=](const QString &key) { + qDebug() << "panel settings changed:" <<key; +- if (getView()->viewId() == "List View" && (key == "date" || key == "hoursystem")) ++ if (getView() && getView()->viewId() == "List View" && (key == "date" || key == "hoursystem")) + refresh(); + }); + } +@@ -117,6 +131,22 @@ DirectoryViewContainer::DirectoryViewContainer(QWidget *parent) : QWidget(parent + if (m_view) + m_view->setCurrentZoomLevel(zoomLevel); + }); ++ ++ connect(m_proxy_model, &FileItemProxyFilterSortModel::sortFinished, this, [=] () { ++ //task 143767, select previous folder when goBack or cdUp ++ //story 23918, improve select effect ++ bool mSelectPreviousFolder = this->property("mSelectPreviousFolder").toBool(); ++ if (m_view && mSelectPreviousFolder){ ++ //add 10ms delay to show and select previous folder ++ QTimer::singleShot(10, this, [=](){ ++ QString mPreviousUri = this->property("mPreviousUri").toString(); ++ qDebug() << "set mPreviousUri:"<<mPreviousUri; ++ m_view->setSelections(QStringList()<<mPreviousUri); ++ m_view->scrollToSelection(mPreviousUri); ++ this->setProperty("mSelectPreviousFolder", false); ++ }); ++ } ++ }); + } + + DirectoryViewContainer::~DirectoryViewContainer() +@@ -156,6 +186,7 @@ void DirectoryViewContainer::goBack() + + auto uri = m_back_list.takeLast(); + m_forward_list.prepend(getCurrentUri()); ++ this->setProperty("mSelectPreviousFolder", true); + Q_EMIT updateWindowLocationRequest(uri, false); + } + +@@ -194,6 +225,7 @@ void DirectoryViewContainer::cdUp() + if (uri.isNull()) + return; + ++ this->setProperty("mSelectPreviousFolder", true); + Q_EMIT updateWindowLocationRequest(uri, true); + } + +@@ -208,6 +240,11 @@ void DirectoryViewContainer::setFilterLabelConditions(QString name) + m_proxy_model->setFilterLabelConditions(name); + } + ++void DirectoryViewContainer::setMutipleLabelConditions(QStringList names, QList<QColor> colors) ++{ ++ m_proxy_model->setMutipleLabelConditions(names, colors); ++} ++ + void DirectoryViewContainer::setShowHidden(bool showHidden) + { + m_proxy_model->setShowHidden(showHidden); +@@ -309,6 +346,7 @@ update: + if (m_view) + m_view->setCurrentZoomLevel(zoomLevel); + ++ this->setProperty("mPreviousUri", m_current_uri); + m_current_uri = uri; + + //special uri process +@@ -616,6 +654,41 @@ void DirectoryViewContainer::updateCurrentFilesThumbnails() + m_model->updateCurrentFilesThumbnails(); + } + ++void DirectoryViewContainer::addFileContentFilter(QString key, bool updateNow) ++{ ++ m_proxy_model->addFileContentFilter(key, updateNow); ++} ++ ++void DirectoryViewContainer::clearFileContentConditions() ++{ ++ m_proxy_model->clearFileContentConditions(); ++} ++ ++void DirectoryViewContainer::clearAllMapsCount() ++{ ++ m_proxy_model->clearAllMapsCount(); ++} ++ ++QMap<int, int> DirectoryViewContainer::getFileTypeCount() ++{ ++ return m_proxy_model->getFileTypeCount(); ++} ++ ++QMap<int, int> DirectoryViewContainer::getFileModifyTimeCount() ++{ ++ return m_proxy_model->getFileModifyTimeCount(); ++} ++ ++QMap<int, int> DirectoryViewContainer::getFileSizeCount() ++{ ++ return m_proxy_model->getFileSizeCount(); ++} ++ ++QMap<int, int> DirectoryViewContainer::getFileLabelCount() ++{ ++ return m_proxy_model->getFileLabelCount(); ++} ++ + void DirectoryViewContainer::addFileDialogFiltersCondition(const QStringList &mimeTypeFilters, const QStringList &nameFilters, QDir::Filters dirFilters, Qt::CaseSensitivity caseSensitivity) + { + if (m_proxy_model) { +diff --git a/libpeony-qt/controls/directory-view/directory-view-container.h b/libpeony-qt/controls/directory-view/directory-view-container.h +index e5fbc33..5792ec7 100644 +--- a/libpeony-qt/controls/directory-view/directory-view-container.h ++++ b/libpeony-qt/controls/directory-view/directory-view-container.h +@@ -104,6 +104,9 @@ Q_SIGNALS: + void signal_itemAdded(const QString& uri);/* 新增文件(夹),item创建完成 */ + void updatePreviewPageRequest(); + void statusBarChanged(); ++ void signal_updateTabPageTitle(const QString& uri); ++ void signal_updateLocationBar(const QString& uri); ++ + + public Q_SLOTS: + void goToUri(const QString &uri, bool addHistory, bool forceUpdate = false); +@@ -131,6 +134,7 @@ public Q_SLOTS: + void setUseDefaultNameSortOrder(bool use); + void setSortFolderFirst(bool folderFirst); + void setFilterLabelConditions(QString name); ++ void setMutipleLabelConditions(QStringList names, QList<QColor> colors); + + //mutiple filter conditions for new advance search + void addFileNameFilter(QString key, bool updateNow = false); +@@ -146,6 +150,15 @@ public Q_SLOTS: + + void updateCurrentFilesThumbnails(); + ++ void addFileContentFilter(QString key, bool updateNow = false); ++ void clearFileContentConditions(); ++ ++ void clearAllMapsCount(); ++ QMap<int, int> getFileTypeCount(); ++ QMap<int, int> getFileModifyTimeCount(); ++ QMap<int, int> getFileSizeCount(); ++ QMap<int, int> getFileLabelCount(); ++ + protected: + /*! + * \brief bindNewProxy +diff --git a/libpeony-qt/controls/directory-view/view/icon-view/icon-view.cpp b/libpeony-qt/controls/directory-view/view/icon-view/icon-view.cpp +index 4a56311..b9b505d 100644 +--- a/libpeony-qt/controls/directory-view/view/icon-view/icon-view.cpp ++++ b/libpeony-qt/controls/directory-view/view/icon-view/icon-view.cpp +@@ -202,6 +202,9 @@ void IconView::setCutFiles(const QStringList &uris) + //FIXME: implement location functions. + void IconView::setDirectoryUri(const QString &uri) + { ++ if (m_current_uri != uri) { ++ disableMultiSelect(); ++ } + m_current_uri = uri; + if (m_current_uri.startsWith("search://")) { + QString nameRegexp = SearchVFSUriParser::getSearchUriNameRegexp(uri); +@@ -405,15 +408,22 @@ void IconView::mousePressEvent(QMouseEvent *e) + else + m_ctrl_key_pressed = false; + +- QModelIndex itemIndex = indexAt(e->pos()); ++ QModelIndex itemIndex = QModelIndex(); ++ for (int i = 0; i < model()->rowCount(); i++) { ++ auto index = model()->index(i, 0); ++ if (visualRect(index).contains(e->pos())) { ++ itemIndex = index; ++ break; ++ } ++ } ++ + if (itemIndex.isValid() && m_multi_select) { + m_mouse_release_unselect = selectedIndexes().contains(itemIndex); + } else { + m_mouse_release_unselect = false; + } + +- auto index = indexAt(e->pos()); +- if (e->button() == Qt::LeftButton && (e->modifiers() & Qt::ControlModifier || selectionMode() == MultiSelection) && selectedIndexes().contains(index)) { ++ if (e->button() == Qt::LeftButton && (e->modifiers() & Qt::ControlModifier || selectionMode() == MultiSelection) && selectedIndexes().contains(itemIndex)) { + m_noSelectOnPress = true; + } else { + m_noSelectOnPress = false; +@@ -790,6 +800,10 @@ void IconView::setIgnore_mouse_move_event(bool ignore_mouse_move_event) + + void IconView::bindModel(FileItemModel *sourceModel, FileItemProxyFilterSortModel *proxyModel) + { ++ // fix: #239734 filedialog has dirty view region ++ if (topLevelWidget()->objectName() != "_peony_mainwindow") ++ setFrameShape(QFrame::NoFrame); ++ + m_model = sourceModel; + m_sort_filter_proxy_model = proxyModel; + +@@ -813,6 +827,11 @@ void IconView::bindModel(FileItemModel *sourceModel, FileItemProxyFilterSortMode + auto currentSelections = selection.indexes(); + + for (auto index : deselection.indexes()) { ++ /* 解决:旧widget未及时delete还能接收鼠标事件,导致图标状态不对也选不中;link to bug#225660. */ ++ auto widget = this->indexWidget(index); ++ if (widget) { ++ widget->hide(); ++ }//end + this->setIndexWidget(index, nullptr); + } + +@@ -1274,6 +1293,7 @@ void IconView2::clearIndexWidget() + m_view->closePersistentEditor(index); + m_view->setIndexWidget(index, nullptr); + } ++ m_view->selectionModel()->clearSelection(); + } + + +diff --git a/libpeony-qt/controls/directory-view/view/list-view/list-view-style.cpp b/libpeony-qt/controls/directory-view/view/list-view/list-view-style.cpp +index 8efbf69..28087a3 100644 +--- a/libpeony-qt/controls/directory-view/view/list-view/list-view-style.cpp ++++ b/libpeony-qt/controls/directory-view/view/list-view/list-view-style.cpp +@@ -94,4 +94,3 @@ void ListViewStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyle + return qApp->style()->drawPrimitive(element, option, painter, widget); + } + } +- +diff --git a/libpeony-qt/controls/directory-view/view/list-view/list-view-style.h b/libpeony-qt/controls/directory-view/view/list-view/list-view-style.h +index 02cc9a4..5344c4b 100644 +--- a/libpeony-qt/controls/directory-view/view/list-view/list-view-style.h ++++ b/libpeony-qt/controls/directory-view/view/list-view/list-view-style.h +@@ -44,7 +44,6 @@ public: + // const QWidget *widget) const override; + // //绘制列表视图文本 + // void viewItemDrawText(QPainter *p, const QStyleOptionViewItem *option, const QRect &rect) const; +- + private: + explicit ListViewStyle(QObject *parent = nullptr); + }; +diff --git a/libpeony-qt/controls/directory-view/view/list-view/list-view.cpp b/libpeony-qt/controls/directory-view/view/list-view/list-view.cpp +index 6985f61..1744ed6 100644 +--- a/libpeony-qt/controls/directory-view/view/list-view/list-view.cpp ++++ b/libpeony-qt/controls/directory-view/view/list-view/list-view.cpp +@@ -119,24 +119,6 @@ ListView::ListView(QWidget *parent) : QTreeView(parent) + header()->setStretchLastSection(false); + header()->setMinimumSectionSize(130); + header()->setTextElideMode(Qt::ElideRight); +- if (this->topLevelWidget()->objectName() == "_peony_mainwindow") { +- connect(header(), &QHeaderView::sectionClicked, this, [=](){ +- //update sort policy +- auto settings = GlobalSettings::getInstance(); +- if (settings->getValue(USE_GLOBAL_DEFAULT_SORTING).toBool()) { +- settings->setValue(SORT_COLUMN, getSortType()); +- settings->setValue(SORT_ORDER, getSortOrder()); +- } else { +- auto metaInfo = FileMetaInfo::fromUri(getDirectoryUri()); +- if (metaInfo) { +- metaInfo->setMetaInfoVariant(SORT_COLUMN, getSortType()); +- metaInfo->setMetaInfoVariant(SORT_ORDER, getSortOrder()); +- } else { +- qCritical()<<"failed to set meta info"<<getDirectoryUri(); +- } +- } +- }); +- } + + connect(header(), &QHeaderView::sectionResized, this, [=]{ + m_header_section_resized_manually = true; +@@ -214,6 +196,28 @@ bool ListView::isDragging() + + void ListView::bindModel(FileItemModel *sourceModel, FileItemProxyFilterSortModel *proxyModel) + { ++ if (topLevelWidget()->objectName() != "_peony_mainwindow") { ++ setFrameShape(QFrame::NoFrame); ++ } else { ++ // note: 如果在构造函数中判断topLevelWidget的objectName,不会找到mainwindow,因为此时还没有设置parent为directoryViewContainer ++ connect(header(), &QHeaderView::sectionClicked, this, [=](){ ++ //update sort policy ++ auto settings = GlobalSettings::getInstance(); ++ if (settings->getValue(USE_GLOBAL_DEFAULT_SORTING).toBool()) { ++ settings->setValue(SORT_COLUMN, getSortType()); ++ settings->setValue(SORT_ORDER, getSortOrder()); ++ } else { ++ auto metaInfo = FileMetaInfo::fromUri(getDirectoryUri()); ++ if (metaInfo) { ++ metaInfo->setMetaInfoVariant(SORT_COLUMN, getSortType()); ++ metaInfo->setMetaInfoVariant(SORT_ORDER, getSortOrder()); ++ } else { ++ qCritical()<<"failed to set meta info"<<getDirectoryUri(); ++ } ++ } ++ }); ++ } ++ + if (!sourceModel || !proxyModel) + return; + m_model = sourceModel; +@@ -413,6 +417,12 @@ void ListView::mousePressEvent(QMouseEvent *e) + setCurrentIndex(index); + return; + } ++ /* hotfix bug#279385 选中一个文件后,鼠标左键点击文管空白位置,文件的选中状态未消失 */ ++ if(e->button() == Qt::LeftButton && (!indexAt(e->pos()).isValid()) ) ++ { ++ this->clearSelection(); ++ return; ++ }//end + + //m_renameTimer + if(!m_renameTimer->isActive()) +@@ -464,12 +474,8 @@ void ListView::mouseMoveEvent(QMouseEvent *e) + QModelIndex itemIndex = indexAt(e->pos()); + if (!itemIndex.isValid()) { + if (QToolTip::isVisible()) { +- QToolTip::hideText(); +- } +- } else { +- if (0 != itemIndex.column() && QToolTip::isVisible()) { +- QToolTip::hideText(); +- } ++ QToolTip::hideText(); ++ } + } + + QTreeView::mouseMoveEvent(e); +@@ -510,7 +516,18 @@ void ListView::mouseMoveEvent(QMouseEvent *e) + void ListView::mouseDoubleClickEvent(QMouseEvent *event) + { + m_editValid = false; +- ++ /** ++ * @bug #250731: [File Manager] Right clicking on the same folder several times in the file manager will take you to the folder ++ * ++ * Prevent double-click events from triggering on right-click ++ * Only double left clicks will be processed ++ * ++ * @author Renyg ++ * @date 2024-08-12 ++ */ ++ if (event->button() == Qt::RightButton) { ++ return; ++ } + QTreeView::mouseDoubleClickEvent(event); + } + +@@ -693,6 +710,8 @@ void ListView::reUpdateScrollBar() + + void ListView::updateGeometries() + { ++ if (m_flag) ++ return; + setUpdatesEnabled(false); + QTreeView::updateGeometries(); + reUpdateScrollBar(); +@@ -1026,6 +1045,9 @@ const QString ListView::getDirectoryUri() + + void ListView::setDirectoryUri(const QString &uri) + { ++ if (m_current_uri != uri) { ++ disableMultiSelect(); ++ } + m_current_uri = uri; + if (m_current_uri.startsWith("search://")) { + QString nameRegexp = SearchVFSUriParser::getSearchUriNameRegexp(uri); +@@ -1078,7 +1100,11 @@ int ListView::getCurrentCheckboxColumn() + { + int section =header()->sectionViewportPosition(3); + int viewportWidth =viewport()->width()+viewport()->x(); ++ + int selectBox = 3; ++ if (m_current_uri.startsWith("trash:///")) { ++ selectBox = 4; ++ } + + for(int i=1;i<=model()->columnCount()-1;i++) + { +@@ -1380,6 +1406,9 @@ void ListView2::bindModel(FileItemModel *model, FileItemProxyFilterSortModel *pr + connect(m_model, &FileItemModel::updated, m_view->viewport(), QOverload<>::of(&QWidget::update)); + + connect(m_view->selectionModel(), &QItemSelectionModel::selectionChanged, this, [=]() { ++ m_view->m_flag = true; ++ m_view->doItemsLayout(); ++ m_view->m_flag = false; + Q_EMIT viewSelectionChanged(); + }); + +@@ -1453,8 +1482,6 @@ void ListView2::bindModel(FileItemModel *model, FileItemProxyFilterSortModel *pr + } + m_need_resize_header = false; + }); +- +- connect(m_view->selectionModel(), &QItemSelectionModel::selectionChanged, m_view, &QTreeView::doItemsLayout); + } + + void ListView2::repaintView() +@@ -1478,4 +1505,5 @@ void ListView2::clearIndexWidget() + m_view->setIndexWidget(index, nullptr); + m_view->closePersistentEditor(index); + } ++ m_view->selectionModel()->clearSelection(); + } +diff --git a/libpeony-qt/controls/directory-view/view/list-view/list-view.h b/libpeony-qt/controls/directory-view/view/list-view/list-view.h +index dfe37eb..1902886 100644 +--- a/libpeony-qt/controls/directory-view/view/list-view/list-view.h ++++ b/libpeony-qt/controls/directory-view/view/list-view/list-view.h +@@ -183,6 +183,7 @@ private: + bool m_editValid; + bool m_ctrl_key_pressed = false; + bool m_delegate_editing = false; ++ bool m_flag = false; + + QRubberBand *m_rubberBand; + QPoint m_lastPressedLogicPoint; +diff --git a/libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp b/libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp +index 3db346c..d01cbd8 100644 +--- a/libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp ++++ b/libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp +@@ -51,7 +51,7 @@ + + #include "volume-manager.h" + +-//#include "properties-window.h" ++#include "properties-window.h" + #include "properties-window-factory-plugin-manager.h" + #include "windows/format_dialog.h" + #include "file-launch-manager.h" +@@ -382,7 +382,20 @@ const QList<QAction *> DirectoryViewMenu::constructOpenOpActions() + connect(l.last(), &QAction::triggered, this, [=]() { + if (!m_top_window) + return; +- m_top_window->goToUri(m_selections.first(), true); ++ ++ if(info->uri().startsWith("file://") && !info->canExecute()){ ++ QMessageBox::critical(nullptr, tr("Open failed"), tr("Open directory failed, you have no permission!")); ++ return; ++ } ++ ++ bool check = Peony::GlobalSettings::getInstance()->getValue(SHOW_IN_NEW_WINDOW).toBool(); ++ if (check) { ++ auto newWindow = dynamic_cast<QWidget *>(m_top_window->create(m_selections.first())); ++ newWindow->setAttribute(Qt::WA_DeleteOnClose); ++ newWindow->show(); ++ } else { ++ m_top_window->goToUri(m_selections.first(), true); ++ } + }); + + auto recommendActions = FileLaunchManager::getRecommendActions(m_selections.first()); +@@ -510,38 +523,30 @@ const QList<QAction *> DirectoryViewMenu::constructOpenOpActions() + connect(l.last(), &QAction::triggered, this, [=]() { + qDebug()<<"triggered"; + QStringList dirs; +- QMap<QString, QStringList> fileMap; +- /**step 1: Categorize files according to type. +- * step 2: Open files in batches to avoid loss of asynchronous messages due to program startup. +- **/ ++ QStringList files; + for (auto uri : m_selections) { + auto info = FileInfo::fromUri(uri); + if (info->isDir() || info->isVolume()) { + dirs<<uri; + } else { +- QString defaultAppName = FileLaunchManager::getDefaultAction(uri)->getAppInfoName(); +- QStringList list; +- if (fileMap.contains(defaultAppName)) { +- list = fileMap[defaultAppName]; +- list << uri; +- fileMap.insert(defaultAppName, list); +- } else { +- list << uri; +- fileMap.insert(defaultAppName, list); +- } ++ files << uri; + } + } +- if (!dirs.isEmpty()) +- m_top_window->addNewTabs(dirs); +- +- if(!fileMap.empty()) { +- QMap<QString, QStringList>::iterator iter = fileMap.begin(); +- while (iter != fileMap.end()) +- { +- FileLaunchManager::openAsync(iter.value()); +- iter++; ++ if (!dirs.isEmpty()) { ++ bool check = Peony::GlobalSettings::getInstance()->getValue(SHOW_IN_NEW_WINDOW).toBool(); ++ if (check) { ++ for (QString uri : dirs) { ++ auto newWindow = dynamic_cast<QWidget *>(m_top_window->create(uri)); ++ newWindow->setAttribute(Qt::WA_DeleteOnClose); ++ newWindow->show(); ++ } ++ } else { ++ m_top_window->addNewTabs(dirs); + } + } ++ ++ if (!files.isEmpty()) ++ FileLaunchManager::openFilesByDefaultApplications(files); + }); + } + } +@@ -749,6 +754,10 @@ const QList<QAction *> DirectoryViewMenu::constructViewOpActions() + if (m_top_window->getCurrentUri() != "trash:///") { + tmp.last()->setVisible(false); + } ++ if(m_top_window->getCurrentUri().startsWith("search:///")){ ++ tmp<<sortTypeMenu->addAction(tr("Path")); ++ } ++ + int sortType = m_view->getSortType(); + if (sortType >= 0) { + tmp.at(sortType)->setCheckable(true); +@@ -776,8 +785,20 @@ const QList<QAction *> DirectoryViewMenu::constructViewOpActions() + tmp.clear(); + //fix bug#97408,change indicator meanings + //箭头向上为升序,向下为降序,与通常的理解对应 +- tmp<<sortOrderMenu->addAction(tr("Descending Order")); +- tmp<<sortOrderMenu->addAction(tr("Ascending Order")); ++ QStringList sortNames; ++ sortNames.append(tr("Descending Order")); ++ sortNames.append(tr("Ascending Order")); ++ if (sortType == 1) { ++ sortNames.clear(); ++ sortNames.append(tr("Newest to oldest")); ++ sortNames.append(tr("Oldest to newest")); ++ } else if (sortType == 3) { ++ sortNames.clear(); ++ sortNames.append(tr("Files from large to small")); ++ sortNames.append(tr("Files from small to large")); ++ } ++ tmp<<sortOrderMenu->addAction(sortNames.at(0)); ++ tmp<<sortOrderMenu->addAction(sortNames.at(1)); + int sortOrder = m_view->getSortOrder(); + tmp.at(sortOrder)->setCheckable(true); + tmp.at(sortOrder)->setChecked(true); +@@ -831,6 +852,29 @@ const QList<QAction *> DirectoryViewMenu::constructFileOpActions() + { + QList<QAction *> l; + ++#if 0 ++ addAction("test set properties", this, [=]{ ++ QDialog d; ++ QVBoxLayout *layout = new QVBoxLayout; ++ auto hiddenBox = new QCheckBox("hidden"); ++ auto readonlyBox = new QCheckBox("readonly"); ++ auto recursiveBox = new QCheckBox("recusive"); ++ layout->addWidget(hiddenBox); ++ layout->addWidget(readonlyBox); ++ layout->addWidget(recursiveBox); ++ auto button = new QPushButton("ok"); ++ layout->addWidget(button); ++ connect(button, &QPushButton::clicked, &d, &QDialog::accept); ++ d.setLayout(layout); ++ if (d.exec()) { ++ bool hidden = hiddenBox->isChecked(); ++ bool readonly = readonlyBox->isChecked(); ++ bool recursive = recursiveBox->isChecked(); ++ FileOperationUtils::setReadOnlyAndHidden(m_selections, readonly, hidden, recursive); ++ } ++ }); ++#endif ++ + if (!m_is_trash && !m_is_computer) { + QString homeUri = "file://" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation); + bool hasStandardPath = FileUtils::containsStandardPath(m_selections); +@@ -879,11 +923,10 @@ const QList<QAction *> DirectoryViewMenu::constructFileOpActions() + auto info = FileInfo::fromUri(actualDir); + if (!info->canWrite()) { + canCut = false; +- if(m_is_search && !m_selections.isEmpty()){ +- auto selectInfo = FileInfo::fromUri(m_selections.first()); +- if(selectInfo->canWrite()) +- canCut = true; +- }//end ++ } ++ if(m_is_search && !m_selections.isEmpty()){/* hotfix bug#222786 */ ++ auto selectInfo = FileInfo::fromUri(m_selections.first()); ++ canCut = FileUtils::isSearchFilesParentWriteable(m_selections, m_is_search); + } + if (canCut) { + l<<addAction(QIcon::fromTheme("edit-cut-symbolic"), tr("Cut")); +@@ -1077,6 +1120,9 @@ const QList<QAction *> DirectoryViewMenu::constructFileOpActions() + }); + } + ++ if (m_selections.isEmpty()) ++ addActions(FileOperationManager::getInstance()->getUndoRedoActions()); ++ + return l; + } + +@@ -1111,13 +1157,22 @@ const QList<QAction *> DirectoryViewMenu::constructFilePropertiesActions() + if (m_selections.isEmpty()) { + QStringList uris; + uris<<m_directory; +- QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(uris); ++ //QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(uris); + //PropertiesWindow *p = new PropertiesWindow(uris); ++ //if(this->parentWidget() && this->parentWidget()->isModal()){ ++ // p->setParent(this->parentWidget()); ++ //} ++ //p->setAttribute(Qt::WA_DeleteOnClose); ++ //PropertiesWindowFactoryPluginManager::getInstance()->show(); ++ //p->show(); + if(this->parentWidget() && this->parentWidget()->isModal()){ +- p->setParent(this->parentWidget()); ++ QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(uris, this->parentWidget()); ++ p->setAttribute(Qt::WA_DeleteOnClose); ++ } else { ++ QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(uris); ++ p->setAttribute(Qt::WA_DeleteOnClose); + } +- p->setAttribute(Qt::WA_DeleteOnClose); +- p->show(); ++ PropertiesWindowFactoryPluginManager::getInstance()->show(); + } else { + QStringList selectUriList; + if (m_selections.first().contains("favorite:///")) { +@@ -1127,12 +1182,21 @@ const QList<QAction *> DirectoryViewMenu::constructFilePropertiesActions() + QStringList urisList; + urisList << FileUtils::getTargetUri(m_selections.at(uriIndex)); + //PropertiesWindow *p = new PropertiesWindow(urisList); +- QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(urisList); ++ //QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(urisList); ++ //if(this->parentWidget() && this->parentWidget()->isModal()){ ++ // p->setParent(this->parentWidget()); ++ //} ++ //p->setAttribute(Qt::WA_DeleteOnClose); ++ //PropertiesWindowFactoryPluginManager::getInstance()->show(); ++ //p->show(); + if(this->parentWidget() && this->parentWidget()->isModal()){ +- p->setParent(this->parentWidget()); ++ QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(urisList, this->parentWidget()); ++ p->setAttribute(Qt::WA_DeleteOnClose); ++ } else { ++ QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(urisList); ++ p->setAttribute(Qt::WA_DeleteOnClose); + } +- p->setAttribute(Qt::WA_DeleteOnClose); +- p->show(); ++ PropertiesWindowFactoryPluginManager::getInstance()->show(); + } else { + selectUriList<< m_selections.at(uriIndex); + } +@@ -1141,26 +1205,44 @@ const QList<QAction *> DirectoryViewMenu::constructFilePropertiesActions() + for(auto &labelUri : m_selections){/* 标记模式页面为不同目录下的文件(夹),所以每个都需要一个属性对话框 */ + QStringList urisList; + urisList.append(labelUri); +- QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(urisList); ++ //QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(urisList); + //PropertiesWindow *p = new PropertiesWindow(urisList); ++ //if(this->parentWidget() && this->parentWidget()->isModal()){ ++ // p->setParent(this->parentWidget()); ++ //} ++ //p->setAttribute(Qt::WA_DeleteOnClose); ++ //PropertiesWindowFactoryPluginManager::getInstance()->show(); ++ //p->show(); + if(this->parentWidget() && this->parentWidget()->isModal()){ +- p->setParent(this->parentWidget()); ++ QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(urisList, this->parentWidget()); ++ p->setAttribute(Qt::WA_DeleteOnClose); ++ } else { ++ QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(urisList); ++ p->setAttribute(Qt::WA_DeleteOnClose); + } +- p->setAttribute(Qt::WA_DeleteOnClose); +- p->show(); ++ PropertiesWindowFactoryPluginManager::getInstance()->show(); + } + }else { + selectUriList = m_selections; + } + + if (selectUriList.count() > 0) { +- QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(selectUriList); ++ //QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(selectUriList); + //PropertiesWindow *p = new PropertiesWindow(selectUriList); ++ //if(this->parentWidget() && this->parentWidget()->isModal()){ ++ // p->setParent(this->parentWidget()); ++ //} ++ //p->setAttribute(Qt::WA_DeleteOnClose); ++ //PropertiesWindowFactoryPluginManager::getInstance()->show(); ++ //p->show(); + if(this->parentWidget() && this->parentWidget()->isModal()){ +- p->setParent(this->parentWidget()); ++ QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(selectUriList, this->parentWidget()); ++ p->setAttribute(Qt::WA_DeleteOnClose); ++ } else { ++ QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(selectUriList); ++ p->setAttribute(Qt::WA_DeleteOnClose); + } +- p->setAttribute(Qt::WA_DeleteOnClose); +- p->show(); ++ PropertiesWindowFactoryPluginManager::getInstance()->show(); + } + } + }); +@@ -1168,13 +1250,22 @@ const QList<QAction *> DirectoryViewMenu::constructFilePropertiesActions() + l<<addAction(QIcon::fromTheme("preview-file"), tr("Properties")); + l.last()->setObjectName(PROPERTIES_ACTION); + connect(l.last(), &QAction::triggered, this, [=]() { +- QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(m_selections); ++ //QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(m_selections); + //PropertiesWindow *p = new PropertiesWindow(m_selections); ++ //if(this->parentWidget() && this->parentWidget()->isModal()){ ++ // p->setParent(this->parentWidget()); ++ //} ++ //p->setAttribute(Qt::WA_DeleteOnClose); ++ //PropertiesWindowFactoryPluginManager::getInstance()->show(); ++ //p->show(); + if(this->parentWidget() && this->parentWidget()->isModal()){ +- p->setParent(this->parentWidget()); ++ QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(m_selections, this->parentWidget()); ++ p->setAttribute(Qt::WA_DeleteOnClose); ++ } else { ++ QMainWindow *p = PropertiesWindowFactoryPluginManager::getInstance()->create(m_selections); ++ p->setAttribute(Qt::WA_DeleteOnClose); + } +- p->setAttribute(Qt::WA_DeleteOnClose); +- p->show(); ++ PropertiesWindowFactoryPluginManager::getInstance()->show(); + }); + } + +@@ -1304,8 +1395,16 @@ const QList<QAction *> DirectoryViewMenu::constructTrashActions() + "these file will not be recoverable.").arg(m_selections.count()); + } + +- result = QMessageBox::question(nullptr, "", message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); +- if (result == QMessageBox::Yes) { ++ ++ QMessageBox msgBox; ++ msgBox.setText(message); ++ msgBox.setIcon(QMessageBox::Question); ++ QPushButton *deleteButton = msgBox.addButton(tr("Delete"), QMessageBox::AcceptRole); ++ msgBox.addButton(tr("Cancel"), QMessageBox::RejectRole); ++ deleteButton->setDefault(true); ++ ++ result = msgBox.exec(); ++ if (msgBox.clickedButton() == deleteButton) { + // SoundEffect::getInstance()->recycleBinClearMusic(); + FileOperationUtils::remove(m_selections); + } +@@ -1465,9 +1564,11 @@ const QList<QAction *> DirectoryViewMenu::constructMenuPluginActions() + + for (auto id : pluginIds) { + auto plugin = MenuPluginManager::getInstance()->getPlugin(id); +- ++ auto tPlugin = MenuPluginManager::getInstance()->getFileSafePlugin(id); ++ auto pluginObj = dynamic_cast<QObject *>(plugin); + if(m_is_filesafe||m_is_filebox_file) { +- if(plugin->name() == tr("Peony-Qt Filesafe Menu Extension") || plugin->name() == tr("Peony File Labels Menu Extension")) { ++ if((plugin && tPlugin && plugin == tPlugin) ++ || pluginObj->property("IsFileSafeShow").toBool()) { + auto actions = plugin->menuActions(MenuPluginInterface::DirectoryView, m_directory, m_selections); + l<<actions; + for (auto action : actions) { +@@ -1477,7 +1578,7 @@ const QList<QAction *> DirectoryViewMenu::constructMenuPluginActions() + } + } + } else { +- if(plugin->name() != tr("Peony-Qt Filesafe Menu Extension")) { ++ if(plugin != tPlugin) { + auto a = Peony::FileOperationManager::getInstance()->isFsynchronizing(); + if(m_is_mobile_file && Peony::FileOperationManager::getInstance()->isFsynchronizing()){ + /* 往移动设备中进行文件拷贝fysnc时导致io阻塞,插件暂先屏蔽,待后续改进 */ +diff --git a/libpeony-qt/controls/menu/menu-plugin-manager.cpp b/libpeony-qt/controls/menu/menu-plugin-manager.cpp +index 4cb2c90..1ca69a5 100644 +--- a/libpeony-qt/controls/menu/menu-plugin-manager.cpp ++++ b/libpeony-qt/controls/menu/menu-plugin-manager.cpp +@@ -73,6 +73,12 @@ bool MenuPluginManager::registerPlugin(MenuPluginInterface *plugin) + if (m_hash.value(plugin->name())) { + return false; + } ++ auto pluginObj = dynamic_cast<QObject *>(plugin); ++ if (pluginObj) { ++ if (pluginObj->property("showInComputerView").toBool()) { ++ m_computerViewPlugins.insert(plugin->name(), plugin); ++ } ++ } + m_hash.insert(plugin->name(), plugin); + return true; + } +@@ -99,6 +105,11 @@ void MenuPluginManager::close() + this->deleteLater(); + } + ++QMap<QString, MenuPluginInterface *> MenuPluginManager::getComputerViewPlugins() const ++{ ++ return m_computerViewPlugins; ++} ++ + const QStringList MenuPluginManager::getPluginIds() + { + return m_hash.keys(); +@@ -109,6 +120,20 @@ MenuPluginInterface *MenuPluginManager::getPlugin(const QString &pluginId) + return m_hash.value(pluginId); + } + ++bool MenuPluginManager::insertFileSafePlugin(MenuPluginInterface *plugin) ++{ ++ if (m_fileSafeHash.value(plugin->name())) { ++ return false; ++ } ++ m_fileSafeHash.insert(plugin->name(), plugin); ++ return true; ++} ++ ++MenuPluginInterface *MenuPluginManager::getFileSafePlugin(const QString &pluginId) ++{ ++ return m_fileSafeHash.value(pluginId); ++} ++ + //CreateLinkInternalPlugin + CreateLinkInternalPlugin::CreateLinkInternalPlugin(QObject *parent) : QObject (parent) + { +@@ -178,7 +203,7 @@ QList<QAction *> CreateLinkInternalPlugin::menuActions(MenuPluginInterface::Type + //FileLabelInternalMenuPlugin + FileLabelInternalMenuPlugin::FileLabelInternalMenuPlugin(QObject *parent) + { +- ++ this->setProperty("IsFileSafeShow", true); + } + + QList<QAction *> FileLabelInternalMenuPlugin::menuActions(MenuPluginInterface::Types types, const QString &uri, const QStringList &selectionUris) +diff --git a/libpeony-qt/controls/menu/menu-plugin-manager.h b/libpeony-qt/controls/menu/menu-plugin-manager.h +index a54689a..f019402 100644 +--- a/libpeony-qt/controls/menu/menu-plugin-manager.h ++++ b/libpeony-qt/controls/menu/menu-plugin-manager.h +@@ -46,10 +46,16 @@ public: + const QStringList getPluginIds(); + MenuPluginInterface *getPlugin(const QString &pluginId); + ++ bool insertFileSafePlugin(MenuPluginInterface *plugin); ++ MenuPluginInterface *getFileSafePlugin(const QString &pluginId); ++ QMap<QString, MenuPluginInterface *> getComputerViewPlugins() const; ++ + void close(); + + private: + QHash<QString, MenuPluginInterface*> m_hash; ++ QHash<QString, MenuPluginInterface*> m_fileSafeHash; ++ QMap<QString, MenuPluginInterface*> m_computerViewPlugins; + + explicit MenuPluginManager(QObject *parent = nullptr); + ~MenuPluginManager(); +diff --git a/libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp b/libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp +index 8b72062..6a63c5d 100644 +--- a/libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp ++++ b/libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp +@@ -32,6 +32,7 @@ + #include "file-utils.h" + #include "file-info.h" + #include "file-info-job.h" ++#include "file-operation-manager.h" + + #include <QAction> + #include <QModelIndex> +@@ -120,7 +121,8 @@ const QList<QAction *> SideBarMenu::constructFavoriteActions() + if(this->parentWidget() && this->parentWidget()->isModal()){ + w->setParent(this->parentWidget()); + } +- w->show(); ++ PropertiesWindowFactoryPluginManager::getInstance()->show(); ++ //w->show(); + }); + if (!m_item->firstColumnIndex().parent().isValid()) { + l.last()->setEnabled(false); +@@ -139,7 +141,8 @@ const QList<QAction *> SideBarMenu::constructPersonalActions() + if(this->parentWidget() && this->parentWidget()->isModal()){ + w->setParent(this->parentWidget()); + } +- w->show(); ++ PropertiesWindowFactoryPluginManager::getInstance()->show(); ++ //w->show(); + }); + + return l; +@@ -151,7 +154,19 @@ const QList<QAction *> SideBarMenu::constructFileSystemItemActions() + QList<QAction *> l; + /* 卸载 */ + bool isWayland = qApp->property("isWayland").toBool(); // related to #105070 +- bool isReddisk = false; ++ bool isCloud = QFile::exists("/etc/ecloud") || QFile::exists("/usr/local/share/Ecloud"); ++ bool hideUnmount = false; ++ bool hideFormat = false; ++ //除samba, ftp, sftp 外的远程目录,云桌面环境下,不显示卸载和格式化选项 ++ if (isCloud) { ++ hideFormat = true; ++ if ( ! m_item->uri().startsWith("smb:///") && ++ ! m_item->uri().startsWith("ftp:///") && ++ ! m_item->uri().startsWith("sftp:///")) { ++ hideUnmount = true; ++ } ++ } ++ + QString unixDevice = m_item->getDevice(); + QString uri; + if(m_uri=="file:///") /* 文件系统特殊处理 */ +@@ -162,14 +177,19 @@ const QList<QAction *> SideBarMenu::constructFileSystemItemActions() + uri=m_uri; + + if (!unixDevice.isEmpty() && uri.isEmpty()) { +- //可能是加密分区数据未同步问题,尝试同步 +- auto fsItem = qobject_cast<SideBarFileSystemItem *>(m_item); +- auto gvolume = fsItem->getVolume().getGVolume(); +- g_autofree gchar *unix_device = g_volume_get_identifier(gvolume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); +- unixDevice = unix_device; +- uri = getComputerUriFromUnixDevice(unixDevice); ++ if ("kyfs" == unixDevice) { ++ uri = getComputerUriFromUri(m_uri); ++ } else { ++ //可能是加密分区数据未同步问题,尝试同步 ++ auto fsItem = qobject_cast<SideBarFileSystemItem *>(m_item); ++ auto gvolume = fsItem->getVolume().getGVolume(); ++ g_autofree gchar *unix_device = g_volume_get_identifier(gvolume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); ++ unixDevice = unix_device; ++ uri = getComputerUriFromUnixDevice(unixDevice); ++ } + } + ++ bool isReddisk = false; + //fix bug#212689, 212690, 213120, 213121, hide reddisk format and unmount option + if (unixDevice.startsWith("/dev/dm") && QFile::exists("/opt/AQTJ/Client/JC/MAIN/bin/jc_main_ui")) + isReddisk = true; +@@ -184,7 +204,8 @@ const QList<QAction *> SideBarMenu::constructFileSystemItemActions() + // } + // } else { + /* 可用的U盘、外接移动硬盘、外接移动光盘, 右键菜单里不允许有“卸载”选项,bug#83206 */ +- if (! isReddisk && !(m_item->isEjectable() || m_item->isStopable()) && m_item->isUnmountable()) { ++ /* 云桌面重定向的盘,不显示卸载选项和格式化选项 bug#255725, task#185121 */ ++ if (! hideFormat && ! isReddisk && !(m_item->isEjectable() || m_item->isStopable()) && m_item->isUnmountable()) { + l<<addAction(QIcon::fromTheme("media-eject-symbolic"), tr("Unmount"), this, [=]() { + m_item->unmount(); + }); +@@ -216,7 +237,7 @@ const QList<QAction *> SideBarMenu::constructFileSystemItemActions() + && (!unixDevice.isNull()) + && !unixDevice.startsWith("/dev/bus/usb") + && (m_item->isVolume()) && !m_item->uri().isEmpty() +- && ! isReddisk; ++ && ! isReddisk && ! hideFormat; + + //fix bug133116, not allow format data disk + if(showFormatDialog && ! isData) +@@ -240,7 +261,8 @@ const QList<QAction *> SideBarMenu::constructFileSystemItemActions() + } + } + #else +- if(!FileUtils::isBusyDevice(m_item->getDevice())){/* 光盘在刻录数据、镜像等操作时,即若处于busy状态时,该菜单置灰不可用。link to bug#143293 */ ++ auto isUdfBusy = Peony::FileOperationManager::getInstance()->isUdfBurnRunning(); ++ if(!FileUtils::isBusyDevice(m_item->getDevice()) && !isUdfBusy){/* 光盘在刻录数据、镜像等操作时,即若处于busy状态时,该菜单置灰不可用。link to bug#143293 */ + UdfBurn::DiscControl *discControl = new UdfBurn::DiscControl(unixDevice); + if(discControl->work()){ + connect(discControl, &UdfBurn::DiscControl::workFinished, this, [=](UdfBurn::DiscControl *discCtrl){ +@@ -294,8 +316,8 @@ const QList<QAction *> SideBarMenu::constructFileSystemItemActions() + QAction *actionBurn = addAction(QIcon::fromTheme("preview-file"), tr("burndata")); + actionBurn->setEnabled(false); + l.append(actionBurn); +- +- if(!FileUtils::isBusyDevice(m_item->getDevice())) { ++ auto isUdfBusy = Peony::FileOperationManager::getInstance()->isUdfBurnRunning(); ++ if(!FileUtils::isBusyDevice(m_item->getDevice()) && !isUdfBusy) { + /* 光盘在刻录数据、镜像等操作时,即若处于busy状态时,该菜单置灰不可用。link to bug#143293 */ + DiscControl *discControl = new DiscControl(unixDevice); + if(discControl->work()){ +@@ -326,7 +348,8 @@ const QList<QAction *> SideBarMenu::constructFileSystemItemActions() + if(this->parentWidget() && this->parentWidget()->isModal()){ + w->setParent(this->parentWidget()); + } +- w->show(); ++ PropertiesWindowFactoryPluginManager::getInstance()->show(); ++ //w->show(); + } + }); + if ((0 != QString::compare(m_uri, "computer:///")) && +@@ -389,7 +412,8 @@ const QList<QAction *> SideBarMenu::constructNetWorkItemActions() + if(this->parentWidget() && this->parentWidget()->isModal()){ + w->setParent(this->parentWidget()); + } +- w->show(); ++ PropertiesWindowFactoryPluginManager::getInstance()->show(); ++ //w->show(); + break; + } + } +@@ -400,7 +424,8 @@ const QList<QAction *> SideBarMenu::constructNetWorkItemActions() + if(this->parentWidget() && this->parentWidget()->isModal()){ + w->setParent(this->parentWidget()); + } +- w->show(); ++ PropertiesWindowFactoryPluginManager::getInstance()->show(); ++ //w->show(); + } + }); + if(m_item->isVolume()) +@@ -430,3 +455,21 @@ QString SideBarMenu::getComputerUriFromUnixDevice(const QString &unixDevice){ + } + return uri; + } ++ ++QString SideBarMenu::getComputerUriFromUri(const QString &uri) ++{ ++ FileEnumerator e; ++ e.setEnumerateDirectory("computer:///"); ++ e.enumerateSync(); ++ QString computerUri = uri; ++ for (auto fileInfo : e.getChildren()) { ++ FileInfoJob infoJob(fileInfo); ++ infoJob.querySync(); ++ if((fileInfo.get()->targetUri() == uri ++ || FileUtils::urlDecode(fileInfo.get()->targetUri()) == uri) && !uri.isEmpty()){ ++ computerUri = fileInfo.get()->uri(); ++ break; ++ } ++ } ++ return computerUri; ++} +diff --git a/libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.h b/libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.h +index b77bb8d..eac86d1 100644 +--- a/libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.h ++++ b/libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.h +@@ -47,6 +47,7 @@ protected: + + private: + QString getComputerUriFromUnixDevice(const QString& unixDevice); ++ QString getComputerUriFromUri(const QString& uri); + + private: + SideBarAbstractItem *m_item = nullptr; +diff --git a/libpeony-qt/controls/multi-select-combobox.cpp b/libpeony-qt/controls/multi-select-combobox.cpp +new file mode 100644 +index 0000000..1ea719c +--- /dev/null ++++ b/libpeony-qt/controls/multi-select-combobox.cpp +@@ -0,0 +1,445 @@ ++/* ++ * Copyright (C) 2024, KylinSoft Co., Ltd. ++ * ++ * 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 3 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, see <https://www.gnu.org/licenses/>. ++ * ++ * Authors: Wenjie Xiang <xiangwenjie@kylinos.cn> ++ * ++ */ ++ ++#include "multi-select-combobox.h" ++#include <QLineEdit> ++#include <QEvent> ++#include <QMouseEvent> ++#include <QPainter> ++#include <QApplication> ++#include <QDebug> ++#include <QPainterPath> ++ ++using namespace Peony; ++MultiSelectComboBox::MultiSelectComboBox(QWidget *parent) ++ : QComboBox(parent) ++{ ++ m_lineEdit = new MultiSelectLineEdit(this); ++ m_lineEdit->setReadOnly(true); ++ this->setLineEdit(m_lineEdit); ++ this->lineEdit()->disconnect(); ++ m_lineEdit->installEventFilter(this); ++ ++ m_listView = new QListView(this); ++ this->setView(m_listView); ++ this->setModel(&m_model); ++ m_listView->setItemDelegate(new MultiSelectComboBoxDelegate(this)); ++ ++ connect(this, QOverload<int>::of(&MultiSelectComboBox::activated), this, &MultiSelectComboBox::slotActivated); ++} ++ ++MultiSelectComboBox::~MultiSelectComboBox() ++{ ++ ++} ++ ++void MultiSelectComboBox::addItem(const QString &text, bool checked, const int count, const QColor &color) ++{ ++ QStandardItem *item = new QStandardItem(text); ++ item->setCheckable(true); ++ item->setCheckState(checked ? Qt::Checked : Qt::Unchecked); ++ item->setData(count, Qt::UserRole + 1); ++ item->setData(color, Qt::UserRole + 2); ++ m_model.appendRow(item); ++ ++ this->updateText(); ++ this->updateMultiComboBoxItemWidth(); ++} ++ ++void MultiSelectComboBox::addItems(const QStringList &texts) ++{ ++ for (auto text : texts) { ++ this->addItem(text); ++ } ++ this->updateMultiComboBoxItemWidth(); ++} ++ ++void MultiSelectComboBox::removeItem(int index) ++{ ++ m_model.removeRow(index); ++ this->updateText(); ++} ++ ++void MultiSelectComboBox::clear() ++{ ++ m_model.clear(); ++ this->updateText(); ++} ++ ++QStringList MultiSelectComboBox::getSelectItemsText() ++{ ++ QStringList texts; ++ for (int i = 0; i < m_model.rowCount(); i++) { ++ QStandardItem *item = m_model.item(i); ++ if (item->checkState() == Qt::Checked) { ++ texts.append(item->text()); ++ } ++ } ++ return texts; ++} ++ ++QList<int> MultiSelectComboBox::getSelectItemsIndex() ++{ ++ QList<int> indexs; ++ for (int i = 0; i < m_model.rowCount(); i++) { ++ QStandardItem *item = m_model.item(i); ++ if (item->checkState() == Qt::Checked) { ++ indexs.append(i); ++ } ++ } ++ return indexs; ++} ++ ++QString MultiSelectComboBox::getItemText(int index) ++{ ++ if (index < 0 || index >= m_model.rowCount()) { ++ return QString(""); ++ } ++ ++ return m_model.item(index)->text(); ++} ++ ++void MultiSelectComboBox::setDefaultItem(int index) ++{ ++ if (index < 0 || index >= m_model.rowCount()) { ++ return; ++ } ++ ++ QStandardItem *item = m_model.item(index); ++ item->setCheckState(Qt::Checked); ++ this->updateText(); ++} ++ ++void MultiSelectComboBox::setEnableItem(int index, bool enabled) ++{ ++ if (index < 0 || index >= m_model.rowCount()) { ++ return; ++ } ++ ++ QStandardItem *item = m_model.item(index); ++ item->setEnabled(enabled); ++ this->updateText(); ++} ++ ++QString MultiSelectComboBox::getShowText() ++{ ++ return m_lineEdit->text(); ++} ++ ++void MultiSelectComboBox::setCheckedItem(int index, bool checked) ++{ ++ if (index < 0 || index >= m_model.rowCount()) { ++ return; ++ } ++ ++ QStandardItem *item = m_model.item(index); ++ item->setCheckState(checked ? Qt::Checked : Qt::Unchecked); ++ this->updateText(); ++} ++ ++void MultiSelectComboBox::uncheckAllItem() ++{ ++ for (int i = 0; i < m_model.rowCount(); i++) { ++ this->setCheckedItem(i); ++ } ++} ++ ++void MultiSelectComboBox::setPlaceholderText(const QString &placeholderText) ++{ ++ m_lineEdit->setPlaceholderText(placeholderText); ++ m_lineEdit->updateWidth(); ++} ++ ++void MultiSelectComboBox::setCountFromItemText(const QString &text, int count) ++{ ++ for(int i = 0; i < m_model.rowCount(); i++) { ++ QStandardItem *item = m_model.item(i); ++ if (item->text() == text) { ++ item->setData(count, Qt::UserRole + 1); ++ break; ++ } ++ } ++} ++ ++void MultiSelectComboBox::setCountFromItemIndex(int index, int count) ++{ ++ QStandardItem *item = m_model.item(index); ++ if (item) { ++ item->setData(count, Qt::UserRole + 1); ++ } ++} ++ ++void MultiSelectComboBox::showPopup() ++{ ++ Q_EMIT showingPopup(); ++ QComboBox::showPopup(); ++} ++ ++void MultiSelectComboBox::hidePopup() ++{ ++ QRect rectView = this->view()->geometry(); ++ rectView.moveTopLeft(this->view()->mapToGlobal(QPoint(0, 0))); ++ ++ QPoint globalPos = QCursor::pos(); ++ ++ if (!rectView.contains(globalPos)) { ++ Q_EMIT hidingPopup(); ++ QComboBox::hidePopup(); ++ } ++} ++ ++void MultiSelectComboBox::mousePressEvent(QMouseEvent *event) ++{ ++ QComboBox::mousePressEvent(event); ++ event->accept(); ++} ++ ++void MultiSelectComboBox::mouseReleaseEvent(QMouseEvent *event) ++{ ++ QComboBox::mouseReleaseEvent(event); ++ event->accept(); ++} ++ ++void Peony::MultiSelectComboBox::mouseMoveEvent(QMouseEvent *event) ++{ ++ QComboBox::mouseMoveEvent(event); ++ event->accept(); ++} ++ ++bool MultiSelectComboBox::eventFilter(QObject *watched, QEvent *event) ++{ ++ if (watched == m_lineEdit && event->type() == QEvent::MouseButtonRelease) { ++ showPopup(); ++ return true; ++ } ++ return false; ++} ++ ++void MultiSelectComboBox::updateText() ++{ ++ QList<QPair<QString, QColor>> items; ++ for (int i = 0; i < m_model.rowCount(); i++) { ++ QStandardItem *item = m_model.item(i); ++ if (item->checkState() == Qt::Checked) { ++ QString text = item->text(); ++ QColor color = item->data(Qt::UserRole + 2).value<QColor>(); ++ items.append(qMakePair(text, color)); ++ } ++ } ++ ++ // 将选中项及颜色传递给自定义 LineEdit ++ dynamic_cast<MultiSelectLineEdit *>(m_lineEdit)->setItems(items); ++} ++ ++void MultiSelectComboBox::slotActivated(int index) ++{ ++ QStandardItem *item = m_model.item(index); ++ if (nullptr == item) { ++ return; ++ } ++ ++ Qt::CheckState state = (item->checkState() == Qt::Checked) ? Qt::Unchecked : Qt::Checked; ++ item->setCheckState(state); ++ ++ this->updateText(); ++ Q_EMIT currentItemStateChanged(); ++} ++ ++void MultiSelectComboBox::updateMultiComboBoxItemWidth() ++{ ++ int maximumWidth = 0; ++ for (int i = 0; i < m_model.rowCount(); i++) { ++ QStandardItem *item = m_model.item(i); ++ QString text = item->text(); ++ QColor color = item->data(Qt::UserRole + 2).value<QColor>(); ++ int offset = (color.isValid() && color != Qt::transparent) ? 25 : 5; ++ //加上间距 ++ offset += 10; ++ offset += fontMetrics().horizontalAdvance(text); ++ //加上后面数字宽度 ++ offset += 80; ++ if (maximumWidth == 0 || offset > maximumWidth) { ++ maximumWidth = offset; ++ } ++ } ++ ++ m_listView->setMinimumWidth(maximumWidth); ++} ++ ++MultiSelectComboBoxDelegate::MultiSelectComboBoxDelegate(QObject *parent) ++ : QStyledItemDelegate(parent) ++{ ++ ++} ++ ++void MultiSelectComboBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const ++{ ++ painter->save(); ++ ++ // 获取数据 ++ QString text = index.data(Qt::DisplayRole).toString(); ++ int count = index.data(Qt::UserRole + 1).toInt(); ++ QColor color = index.data(Qt::UserRole + 2).value<QColor>(); ++ bool isChecked = index.data(Qt::CheckStateRole).toInt() == Qt::Checked; ++ QRect rect = option.rect; ++ int checkIconWidth = 16; ++ int circleDiameter = 12; ++ int countTextWidth = 60; ++ ++ // 绘制选中项背景为圆角矩形 ++ if (option.state & QStyle::State_Selected) { ++ QPainterPath path; ++ path.addRoundedRect(rect.adjusted(2, 2, -2, -2), 6, 6); // 圆角矩形 ++ painter->fillPath(path, option.palette.highlight()); ++ } ++ ++ // 绘制勾选图标 ++ if (isChecked) { ++ QRect checkRect(rect.left() + 5, rect.top() + (rect.height() - checkIconWidth) / 2, checkIconWidth, checkIconWidth); // 勾选图标区域 ++ QPixmap checkIcon = QApplication::style()->standardPixmap(QStyle::SP_DialogApplyButton); ++ painter->drawPixmap(checkRect, checkIcon); ++ } ++ ++ int offset = (color.isValid() && color != Qt::transparent) ? 25 : 5; ++ QRect circleRect(rect.left() + offset, rect.top() + (rect.height() - circleDiameter) / 2, circleDiameter, circleDiameter); ++ painter->setBrush(color); ++ painter->setPen(Qt::NoPen); ++ if (color.isValid()) ++ painter->drawEllipse(circleRect); ++ ++ // 绘制文字 ++ int textX = circleRect.right() + 10; // 文字起始位置 ++ int textWidth = rect.width() - textX - countTextWidth; // 文字区域宽度 ++ QRect textRect(textX, rect.top(), textWidth, rect.height()); ++ painter->setPen(option.palette.text().color()); ++ painter->drawText(textRect, Qt::AlignVCenter | Qt::AlignLeft, text); ++ ++ // 绘制数量 ++ QRect countRect(rect.right() - countTextWidth - 10, rect.top(), countTextWidth, rect.height()); ++ painter->setPen(QColor(0xA6A6A6)); ++ painter->drawText(countRect, Qt::AlignVCenter | Qt::AlignRight, QString::number(count)); ++ ++ painter->restore(); ++} ++ ++QSize MultiSelectComboBoxDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const ++{ ++ QSize size = QStyledItemDelegate::sizeHint(option, index); ++ size.setHeight(36); ++ return size; ++} ++ ++MultiSelectLineEdit::MultiSelectLineEdit(QWidget *parent) ++ : QLineEdit(parent), m_items() ++{ ++ ++} ++ ++MultiSelectLineEdit::~MultiSelectLineEdit() ++{ ++ ++} ++ ++void MultiSelectLineEdit::setItems(const QList<QPair<QString, QColor> > &items) ++{ ++ m_items = items; ++ updateWidth(); ++} ++ ++int MultiSelectLineEdit::calculateContentWidth() const ++{ ++ int width = 5; ++ QFontMetrics metrics(font()); ++ ++ if (m_items.isEmpty()) { ++ width += metrics.horizontalAdvance(this->placeholderText()) + 10; ++ } else { ++ for(int i = 0; i < m_items.size(); i++) { ++ if (m_items[i].second.isValid() && m_items[i].second != Qt::transparent) { ++ width += m_radius + 5; ++ } ++ width += metrics.horizontalAdvance(m_items[i].first) + 5; ++ ++ const QColor &color = m_items[i].second; ++ bool isValid = (color.isValid() && color != Qt::transparent); ++ if (i != m_items.size() - 1) { ++ QString separator = isValid ? " " : ", "; ++ width += fontMetrics().horizontalAdvance(separator); ++ } ++ } ++ } ++ ++ return width; ++} ++ ++void MultiSelectLineEdit::updateWidth() ++{ ++ int width = calculateContentWidth(); ++ setFixedWidth(width + 10); ++ update(); ++ ++ if (parentWidget()) { ++ parentWidget()->setFixedWidth(width + 35); ++ } ++} ++ ++void MultiSelectLineEdit::paintEvent(QPaintEvent *event) ++{ ++ Q_UNUSED(event); ++ QPainter painter(this); ++ painter.setRenderHint(QPainter::Antialiasing); ++ ++ int x = 5; // 起始位置 ++ int y = (height() - fontMetrics().height()) / 2; // 垂直居中 ++ ++ for (int i = 0; i < m_items.size(); i++) { ++ const QString &text = m_items[i].first; ++ const QColor &color = m_items[i].second; ++ ++ bool isValid = (color.isValid() && color != Qt::transparent); ++ if (isValid) { ++ int topY = (height() - m_radius) / 2; ++ painter.setBrush(color); ++ painter.setPen(Qt::NoPen); ++ painter.drawEllipse(x, topY, m_radius, m_radius); ++ x += m_radius + 5; // 移动位置 ++ } ++ ++ // 绘制文字 ++ painter.setPen(palette().text().color()); ++ painter.drawText(x, y + fontMetrics().ascent(), text); ++ ++ x += fontMetrics().horizontalAdvance(text) + 5; // 文字宽度 + 间隔 ++ ++ if (i != m_items.size() - 1) { ++ QString separator = isValid ? " " : ", "; ++ painter.drawText(x, y + fontMetrics().ascent(), separator); ++ x += fontMetrics().horizontalAdvance(separator); ++ } ++ } ++ ++ if (m_items.isEmpty()) { ++ if (!this->placeholderText().isEmpty()) { ++ QString text = this->placeholderText(); ++ painter.setPen(palette().placeholderText().color()); ++ painter.drawText(x, y + fontMetrics().ascent(), text); ++ } ++ } ++} +diff --git a/libpeony-qt/controls/multi-select-combobox.h b/libpeony-qt/controls/multi-select-combobox.h +new file mode 100644 +index 0000000..5cf2145 +--- /dev/null ++++ b/libpeony-qt/controls/multi-select-combobox.h +@@ -0,0 +1,113 @@ ++/* ++ * Copyright (C) 2024, KylinSoft Co., Ltd. ++ * ++ * 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 3 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, see <https://www.gnu.org/licenses/>. ++ * ++ * Authors: Wenjie Xiang <xiangwenjie@kylinos.cn> ++ * ++ */ ++ ++#ifndef MULTISELECTCOMBOBOX_H ++#define MULTISELECTCOMBOBOX_H ++ ++#include "peony-core_global.h" ++#include <QObject> ++#include <QComboBox> ++#include <QListView> ++#include <QStandardItemModel> ++#include <QStyledItemDelegate> ++#include <QLineEdit> ++ ++namespace Peony { ++ ++class PEONYCORESHARED_EXPORT MultiSelectComboBoxDelegate : public QStyledItemDelegate ++{ ++ Q_OBJECT ++public: ++ explicit MultiSelectComboBoxDelegate(QObject *parent = nullptr); ++ ++ void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; ++ ++ QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; ++}; ++ ++class PEONYCORESHARED_EXPORT MultiSelectLineEdit : public QLineEdit ++{ ++ Q_OBJECT ++public: ++ explicit MultiSelectLineEdit(QWidget *parent = nullptr); ++ ~MultiSelectLineEdit(); ++ ++ void setItems(const QList<QPair<QString, QColor>> &items); ++ int calculateContentWidth() const; ++ void updateWidth(); ++ ++protected: ++ void paintEvent(QPaintEvent *event) override; ++ ++private: ++ QList<QPair<QString, QColor>> m_items; // 存储选项及其颜色 ++ int m_radius = 12; ++}; ++ ++class PEONYCORESHARED_EXPORT MultiSelectComboBox : public QComboBox ++{ ++ Q_OBJECT ++public: ++ explicit MultiSelectComboBox(QWidget *parent = nullptr); ++ ~MultiSelectComboBox(); ++ void addItem(const QString &text, bool checked = false, const int count = 0, const QColor &color = Qt::transparent); ++ void addItems(const QStringList &texts); ++ void removeItem(int index); ++ void clear(); ++ QStringList getSelectItemsText(); ++ QList<int> getSelectItemsIndex(); ++ QString getItemText(int index); ++ void setDefaultItem(int index); ++ void setEnableItem(int index, bool enabled = true); ++ QString getShowText(); ++ void setCheckedItem(int index, bool checked = false); ++ void uncheckAllItem(); ++ void setPlaceholderText(const QString &placeholderText); ++ void setCountFromItemText(const QString &text, int count); ++ void setCountFromItemIndex(int index, int count); ++ ++Q_SIGNALS: ++ void showingPopup(); ++ void hidingPopup(); ++ void currentItemStateChanged(); ++ ++protected: ++ void showPopup(); ++ void hidePopup(); ++ virtual void mousePressEvent(QMouseEvent *event); ++ virtual void mouseReleaseEvent(QMouseEvent *event); ++ virtual void mouseMoveEvent(QMouseEvent *event); ++ virtual bool eventFilter(QObject *watched, QEvent *event); ++ ++private: ++ void updateText(); ++ ++private Q_SLOTS: ++ void slotActivated(int index); ++ void updateMultiComboBoxItemWidth(); ++ ++private: ++ MultiSelectLineEdit *m_lineEdit; ++ QListView *m_listView; ++ QStandardItemModel m_model; ++}; ++ ++} ++#endif // MULTISELECTCOMBOBOX_H +diff --git a/libpeony-qt/controls/navigation-bar/advanced-location-bar.cpp b/libpeony-qt/controls/navigation-bar/advanced-location-bar.cpp +index e925915..70ad9a8 100644 +--- a/libpeony-qt/controls/navigation-bar/advanced-location-bar.cpp ++++ b/libpeony-qt/controls/navigation-bar/advanced-location-bar.cpp +@@ -36,9 +36,10 @@ using namespace Peony; + + AdvancedLocationBar::AdvancedLocationBar(QWidget *parent) : QWidget(parent) + { +- QStackedLayout *layout = new QStackedLayout(this); ++ QStackedLayout *layout = new QStackedLayout; ++ QHBoxLayout *mainlayout = new QHBoxLayout(this); + m_layout = layout; +- ++ mainlayout->setContentsMargins(0, 0, 0, 0); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + layout->setSizeConstraint(QLayout::SetDefaultConstraint); +@@ -92,8 +93,27 @@ AdvancedLocationBar::AdvancedLocationBar(QWidget *parent) : QWidget(parent) + auto key = m_search_bar->text(); + key = processSpecialChar(key); + qDebug() << "search key:" <<key <<m_last_key; ++ // To fix the issue where the current search path is updated when switching tab ++ if (m_text.startsWith("search:///")) { ++ QString currentSearchPath = Peony::SearchVFSUriParser::getSearchUriPath(m_text); ++ if (!currentSearchPath.isEmpty() && currentSearchPath != m_last_non_search_path) { ++ if (key != m_last_key) ++ { ++ Q_EMIT searchRequest(currentSearchPath, key); ++ m_last_key = key; ++ if (key == "") { ++ m_search_bar->updateSearchProgress(false); ++ } else { ++ m_search_bar->updateSearchProgress(true); ++ } ++ } ++ return; ++ } ++ } ++ + if (key != m_last_key) + { ++ m_in_search_mode = true; + Q_EMIT searchRequest(m_last_non_search_path, key); + m_last_key = key; + if (key == "") { +@@ -104,6 +124,12 @@ AdvancedLocationBar::AdvancedLocationBar(QWidget *parent) : QWidget(parent) + } + }); + ++ m_search_bar->connect(m_search_bar, &Peony::SearchBarContainer::updateLastLocationPath, [=]() { ++ //关闭搜索后,需要更新路径 ++ m_in_search_mode = false; ++ Q_EMIT searchRequest(m_last_non_search_path, ""); ++ }); ++ + m_search_bar->connect(m_search_bar, &Peony::SearchBarContainer::filterUpdate, [=](const int &index) + { + Q_EMIT this->updateFileTypeFilter(index); +@@ -121,7 +147,10 @@ AdvancedLocationBar::AdvancedLocationBar(QWidget *parent) : QWidget(parent) + + layout->addWidget(m_bar); + layout->addWidget(m_edit); +- layout->addWidget(m_search_bar); ++ ++ mainlayout->addLayout(layout); ++ mainlayout->addWidget(m_search_bar); ++ setLayout(mainlayout); + + setLayout(layout); + auto iscleaned = Peony::TrashCleanedWatcher::getInstance(); +@@ -153,13 +182,19 @@ void AdvancedLocationBar::updateLocation(const QString &uri) + m_edit->setUri(uri); + m_text = uri; + //qDebug() << "m_edit visible:"<<isEditing(); +- if (! uri.startsWith("search://")) +- { ++ if (! uri.startsWith("search://")) { + m_last_non_search_path = uri; + //from search mode go to other non search path, stop search + //fix bug#97807, change path in search mode crash issue + if (m_last_key != "") + clearSearchBox(); ++ } else { ++ QString key = SearchVFSUriParser::getSearchUriNameRegexp(uri); ++ if (key != m_last_key) { ++ m_search_bar->setText(key); ++ m_search_bar->setFocus(); ++ m_last_key = key; ++ } + } + Q_EMIT this->refreshRequest(); + } +@@ -168,6 +203,17 @@ void AdvancedLocationBar::setAnimationMode(bool isAnimation) + m_bar->setAnimationMode(isAnimation); + } + ++void AdvancedLocationBar::setSearchBarFocus() ++{ ++ if (m_search_bar) ++ return m_search_bar->setFocus(); ++} ++ ++void AdvancedLocationBar::setSearchText(const QString &text) ++{ ++ m_search_bar->setText(text); ++} ++ + bool AdvancedLocationBar::isEditing() + { + return m_edit->isVisible(); +@@ -195,9 +241,7 @@ void AdvancedLocationBar::switchEditMode(bool bSearchMode) + { + if (bSearchMode) + { +- m_edit->setVisible(false); +- m_layout->setCurrentWidget(m_search_bar); +- m_search_bar->setPlaceholderText(tr("Search Content...")); ++ m_search_bar->setPlaceholderText(tr("Search File")); + m_search_bar->setFocus(); + m_in_search_mode = true; + } +diff --git a/libpeony-qt/controls/navigation-bar/advanced-location-bar.h b/libpeony-qt/controls/navigation-bar/advanced-location-bar.h +index 3ca3e76..82e11a4 100644 +--- a/libpeony-qt/controls/navigation-bar/advanced-location-bar.h ++++ b/libpeony-qt/controls/navigation-bar/advanced-location-bar.h +@@ -41,6 +41,12 @@ public: + explicit AdvancedLocationBar(QWidget *parent = nullptr); + bool isEditing(); + void setAnimationMode(bool isAnimation); ++ void setSearchBarFocus(); ++ bool getSearchMode() { ++ return m_in_search_mode; ++ } ++ ++ void setSearchText(const QString& text); + + Q_SIGNALS: + void updateWindowLocationRequest(const QString &uri, bool addHistory = true, bool forceUpdate = false); +diff --git a/libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp b/libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp +index 8f233c4..477a2ed 100644 +--- a/libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp ++++ b/libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp +@@ -56,6 +56,7 @@ + #include <QProxyStyle> + #include <QStyleOptionToolButton> + #include <syslog.h> ++#include <algorithm> + + using namespace Peony; + +@@ -398,6 +399,7 @@ void LocationBar::addButton(const QString &uri, bool setIcon, bool setMenu) + button->setPopupMode(QToolButton::MenuButtonPopup); + + auto completeName = FileUtils::getFileDisplayName(uri); ++ button->setProperty("completeName", completeName); + QString displayName = completeName; + m_buttons.insert(QUrl(uri).toEncoded(), button); + if (m_current_uri.startsWith("search://")) { +@@ -429,31 +431,16 @@ void LocationBar::addButton(const QString &uri, bool setIcon, bool setMenu) + //comment to fix button text show incomplete issue, link to bug#72080 + //button->setStyleSheet("QToolButton{padding-left: 13px; padding-right: 13px}"); + ++ /* hotfix bug#226649 【文件管理器】安装系统时选择英文,安装后切换系统到中文,文件管理器图片等位置的路径栏也翻译成中文,但是点击回车会弹窗报错 + //fix bug#84324 + // QUrl url = uri; + QUrl url = FileUtils::urlEncode(uri); + if (!url.fileName().isEmpty()) + { +- button->setText(displayName); + m_current_uri = uri.left(uri.lastIndexOf("/")+1) + displayName; +- } else { +- if (uri == "file:///") { +-// auto text = FileUtils::getFileDisplayName("computer:///root.link"); +-// if (text.isNull()) { +-// text = tr("File System"); +-// } +- //fix bug#47597, show as root.link issue +- QString text = tr("File System"); +- button->setText(text); +- //comment to fix button text show incomplete issue, link to bug#72080 +- //button->setStyleSheet("QToolButton{padding-left: 15px; padding-right: 15px}"); +- } else { +- button->setText(displayName); +- } +- } ++ } */ + + //if button text is too long, elide it +- displayName = button->text(); + if (displayName.length() > ELIDE_TEXT_LENGTH) + { + int charWidth = fontMetrics().averageCharWidth(); +@@ -605,26 +592,150 @@ void LocationBar::setAnimationMode(bool isAnimation) + + void LocationBar::doLayout() + { +- m_indicator->setVisible(false); ++ static int charWidth = fontMetrics().averageCharWidth(); ++ initLayout(); ++ ++ // Handle the truncation logic ++ if (m_iVisibleButtonCount < 2 && m_buttons.count() >= 2) { ++ auto buttonSecondLast = m_buttons.values().at(m_buttons.count() - 2); ++ auto buttonLast = m_buttons.values().last(); ++ ++ int buttonLastWidth = buttonLast->sizeHint().width(); ++ int buttonSecondLastWidth = m_iTotalWidth - buttonLastWidth; ++ bool isbuttonLastWidthChange = false; ++ // If the width of the second-to-last button is less than the minimum folder name length ++ if (buttonSecondLastWidth <= MIN_FOLDER_NAME_LENGTH * charWidth) { ++ buttonSecondLastWidth = MIN_FOLDER_NAME_LENGTH * charWidth; ++ buttonLastWidth = m_iTotalWidth - buttonSecondLastWidth; ++ isbuttonLastWidthChange = true; ++ } + +- QList<int> sizeHints; ++ QString completeNameBtnSecondLast = buttonSecondLast->property("completeName").toString(); ++ QString completeNameBtnLast = buttonLast->property("completeName").toString(); ++ // Truncate the text of the second-to-last button ++ if (completeNameBtnSecondLast.length() * charWidth > buttonSecondLastWidth) ++ { ++ int minValue = std::min({charWidth * (buttonSecondLastWidth / charWidth), charWidth * ELIDE_TEXT_LENGTH, m_iTotalWidth}); ++ completeNameBtnSecondLast = fontMetrics().elidedText(completeNameBtnSecondLast, Qt::ElideRight, minValue); ++ } + +- m_indicator_menu->clear(); ++ if (completeNameBtnSecondLast != buttonSecondLast->text()) { ++ buttonSecondLast->setText(completeNameBtnSecondLast); ++ } ++ buttonSecondLast->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); ++ buttonSecondLast->setVisible(true); ++ buttonSecondLast->adjustSize(); ++ ++ buttonSecondLast->move(m_iOffset, 0); ++ m_iOffset += buttonSecondLast->width(); ++ ++ // Truncate the text of the last button ++ //buttonLastWidth = m_iTotalWidth - buttonSecondLast->width() + m_iconWidth - 10; ++ buttonLastWidth = this->width() - m_iOffset - 28; ++ if (completeNameBtnLast.length() * charWidth > buttonLastWidth) ++ { ++ int minValue = std::min({charWidth * (buttonLastWidth / charWidth), charWidth * ELIDE_TEXT_LENGTH}); ++ completeNameBtnLast = fontMetrics().elidedText(completeNameBtnLast, Qt::ElideRight, minValue); ++ } ++ ++ if (completeNameBtnLast != buttonLast->text()) { ++ buttonLast->setText(completeNameBtnLast); ++ } ++ ++ buttonLast->setVisible(true); ++ if (isbuttonLastWidthChange) { ++ buttonLast->adjustSize(); ++ } ++ ++ buttonLast->move(m_iOffset, 0); ++ } else { ++ // Display the visible buttons ++ for (int index = m_sizeHints.count() - m_iVisibleButtonCount; index < m_sizeHints.count(); index++) { ++ if (index >= 0 && index < m_buttons.values().size()) { ++ auto button = m_buttons.values().at(index); ++ button->setVisible(true); ++ button->move(m_iOffset, 0); ++ if (index == m_sizeHints.count() - m_iVisibleButtonCount) { ++ button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); ++ button->adjustSize(); ++ } ++ m_iOffset += button->width(); ++ } ++ } ++ } + +- int iconWidth = 0; ++ int spaceCount = 0; ++ QList<QAction *> actions; ++ for (auto button : m_buttons) { ++ if (button->isVisible()) { ++ break; ++ } ++ auto uri = button->property("uri").toString(); ++ QString space; ++ int i = 0; ++ while (i < spaceCount) { ++ space.append(' '); ++ i++; ++ } ++ auto action = new QAction(space + button->text(), m_indicator_menu); ++ actions.append(action); ++ ++ connect(action, &QAction::triggered, this, [=](){ ++ Q_EMIT groupChangedRequest(uri); ++ }); ++ spaceCount++; ++ } ++ m_indicator_menu->addActions(actions); ++ //add some space for switch to edit ++ for (int i = 0; i < 10; i++) { ++ m_indicator_menu->addSeparator(); ++ } ++} ++ ++void LocationBar::initLayout() ++{ ++ static int charWidth = fontMetrics().averageCharWidth(); ++ m_indicator->setVisible(false); ++ m_sizeHints.clear(); ++ m_indicator_menu->clear(); ++ m_iconWidth = 0; + if (!m_buttons.isEmpty()) { + auto button = m_buttons.first(); + button->setVisible(true); + button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); +- iconWidth = button->sizeHint().width(); ++ m_iconWidth = button->sizeHint().width(); + button->setToolButtonStyle(Qt::ToolButtonTextOnly); +- iconWidth = iconWidth - button->sizeHint().width(); ++ m_iconWidth = m_iconWidth - button->sizeHint().width(); + button->setVisible(false); + } + int exceptTotalwidth = 0; ++ ++ // restore the text of the last two buttons ++ if (m_buttons.count() >= 2) { ++ auto buttonSecondLast = m_buttons.values().at(m_buttons.count() - 2); ++ auto buttonLast = m_buttons.values().last(); ++ QString completeNameBtnSecondLast = buttonSecondLast->property("completeName").toString(); ++ QString completeNameBtnLast = buttonLast->property("completeName").toString(); ++ completeNameBtnSecondLast = fontMetrics().elidedText(completeNameBtnSecondLast, Qt::ElideRight, charWidth * ELIDE_TEXT_LENGTH); ++ ++ if (buttonSecondLast->text() != completeNameBtnSecondLast) { ++ buttonSecondLast->setText(completeNameBtnSecondLast); ++ buttonSecondLast->setVisible(true); ++ buttonSecondLast->adjustSize(); ++ buttonSecondLast->setVisible(false); ++ } ++ ++ completeNameBtnLast = fontMetrics().elidedText(completeNameBtnLast, Qt::ElideRight, charWidth * ELIDE_TEXT_LENGTH); ++ if (buttonLast->text() != completeNameBtnLast) { ++ buttonLast->setText(completeNameBtnLast); ++ buttonLast->setVisible(true); ++ buttonLast->adjustSize(); ++ buttonLast->setVisible(false); ++ } ++ } ++ + for (auto button : m_buttons) { + button->setVisible(true); +- // 默认不做自动文字缩略 + button->setProperty("elideText", QVariant()); + button->resize(button->sizeHint().width(), button->height()); + button->setFixedHeight(this->height()); //fixme +@@ -632,84 +743,36 @@ void LocationBar::doLayout() + button->adjustSize(); + int sizeHintWidth = button->sizeHint().width(); + exceptTotalwidth += sizeHintWidth; +- sizeHints<<sizeHintWidth; ++ m_sizeHints << sizeHintWidth; + button->setVisible(false); + } + +- int totalWidth = this->width() - iconWidth; +- if (totalWidth < exceptTotalwidth) { +- totalWidth = totalWidth - m_indicator->width() - 2; ++ m_iTotalWidth = this->width() - m_iconWidth; ++ if (m_iTotalWidth < exceptTotalwidth) { ++ m_iTotalWidth = m_iTotalWidth - m_indicator->width() - 2; + } + + int currentWidth = 0; +- int visibleButtonCount = 0; +- for (int index = sizeHints.count() - 1; index >= 0; index--) { +- int tmp = currentWidth + sizeHints.at(index); +- if (tmp <= totalWidth) { +- visibleButtonCount++; ++ m_iVisibleButtonCount = 0; ++ for (int index = m_sizeHints.count() - 1; index >= 0; index--) { ++ int tmp = currentWidth + m_sizeHints.at(index); ++ if (tmp <= m_iTotalWidth) { ++ m_iVisibleButtonCount++; + currentWidth = tmp; + } else { + break; + } + } + +- int offset = 0; ++ m_iOffset = 0; + +- bool indicatorVisible = visibleButtonCount < sizeHints.count(); ++ bool indicatorVisible = m_iVisibleButtonCount < m_sizeHints.count(); + if (indicatorVisible) { + m_indicator->setVisible(true); +- offset += m_indicator->width() + 2; ++ m_iOffset += m_indicator->width() + 2; + } else { + m_indicator->setVisible(false); + } +- +- for (int index = sizeHints.count() - visibleButtonCount; index < sizeHints.count(); index++) { +- auto button = m_buttons.values().at(index); +- button->setVisible(true); +- button->move(offset, 0); +- if (index == sizeHints.count() - visibleButtonCount) { +- button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); +- button->adjustSize(); +- } +- offset += button->width(); +- } +- +- if (visibleButtonCount == 0 && !m_buttons.isEmpty()) { +- auto button = m_buttons.values().at(sizeHints.count() - 1); +- button->setVisible(true); +- // 设置自动文字缩略 +- button->setProperty("elideText", true); +- button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); +- button->move(offset, 0); +- button->resize(totalWidth, button->height()); +- } +- +- int spaceCount = 0; +- QList<QAction *> actions; +- for (auto button : m_buttons) { +- if (button->isVisible()) { +- break; +- } +- auto uri = button->property("uri").toString(); +- QString space; +- int i = 0; +- while (i < spaceCount) { +- space.append(' '); +- i++; +- } +- auto action = new QAction(space + button->text(), m_indicator_menu); +- actions.append(action); +- +- connect(action, &QAction::triggered, this, [=](){ +- Q_EMIT groupChangedRequest(uri); +- }); +- spaceCount++; +- } +- m_indicator_menu->addActions(actions); +- //add some space for switch to edit +- for (int i = 0; i < 10; i++) { +- m_indicator_menu->addSeparator(); +- } + } + + void LocationBarButtonStyle::polish(QWidget *widget) +@@ -742,6 +805,7 @@ void LocationBarButtonStyle::drawComplexControl(QStyle::ComplexControl control, + } else { + opt.rect.adjust(0, 1, 0, -1); //bug#165286 地址栏中“计算机”文字显示不完整,高度减小2,宽度不变 + } ++#if 0 + if (widget) { + if (widget->property("elideText").toBool()) { + // 设置文字缩略 +@@ -750,6 +814,7 @@ void LocationBarButtonStyle::drawComplexControl(QStyle::ComplexControl control, + opt.text = opt.fontMetrics.elidedText(text, Qt::ElideRight, textWidth); + } + } ++#endif + return qApp->style()->drawComplexControl(control, &opt, painter, widget); + } + return qApp->style()->drawComplexControl(control, option, painter, widget); +diff --git a/libpeony-qt/controls/navigation-bar/location-bar/location-bar.h b/libpeony-qt/controls/navigation-bar/location-bar/location-bar.h +index 527a403..8d79a86 100644 +--- a/libpeony-qt/controls/navigation-bar/location-bar/location-bar.h ++++ b/libpeony-qt/controls/navigation-bar/location-bar/location-bar.h +@@ -64,8 +64,16 @@ protected: + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *event) override; + ++ /** ++ * @brief Performs the layout of the LocationBar. ++ */ + void doLayout(); + ++ /** ++ * @brief Initializes the layout of the LocationBar. ++ */ ++ void initLayout(); ++ + private: + QString m_current_uri; + QLineEdit *m_styled_edit; +@@ -77,6 +85,7 @@ private: + bool m_isAnimation = false; //在动画过程中不会重新布局 + + const int ELIDE_TEXT_LENGTH = 16; ++ const int MIN_FOLDER_NAME_LENGTH = 4; + + std::shared_ptr<FileInfo> m_current_info; + QList<std::shared_ptr<FileInfo>> m_buttons_info; +@@ -84,6 +93,11 @@ private: + QHash<QString, QList<std::shared_ptr<FileInfo>>> m_infos_hash; + + bool m_is_classical = false; ++ QList<int> m_sizeHints; ++ int m_iTotalWidth = 0; ++ int m_iOffset = 0; ++ int m_iVisibleButtonCount = 0; ++ int m_iconWidth = 0; + }; + + } +diff --git a/libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp b/libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp +index b7bd384..4df83ad 100644 +--- a/libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp ++++ b/libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp +@@ -72,7 +72,7 @@ DefaultPreviewPage::DefaultPreviewPage(QWidget *parent) : QStackedWidget (parent + { + setContentsMargins(10, 20, 10, 20); + +- auto label = new QLabel(tr("Select the file you want to preview..."), this); ++ auto label = new QLabel(tr("Select the file you want to preview"), this); + label->setWordWrap(true); + label->setAlignment(Qt::AlignCenter); + m_empty_tab_widget = label; +@@ -213,7 +213,7 @@ void DefaultPreviewPage::cancel() + m_preview_tab_widget->cancel(); + setCurrentWidget(m_empty_tab_widget); + QLabel *label = qobject_cast<QLabel*>(m_empty_tab_widget); +- label->setText(tr("Select the file you want to preview...")); ++ label->setText(tr("Select the file you want to preview")); + } + + void DefaultPreviewPage::closePreviewPage() +@@ -247,6 +247,7 @@ FilePreviewPage::FilePreviewPage(QWidget *parent) : QFrame(parent) + m_form->setContentsMargins(0, 0, 0, 0); + + m_display_name_label = new QLabel(this); ++ this->setLabelProperties(m_display_name_label); + QFont font; + font.setBold(true); + m_display_name_label->setFont(font); +@@ -255,6 +256,7 @@ FilePreviewPage::FilePreviewPage(QWidget *parent) : QFrame(parent) + m_layout->addSpacing(16); + + m_type_label = new QLabel(this); ++ this->setLabelProperties(m_type_label); + m_form->addRow(tr("File Type:"), m_type_label); + m_type_label->setAlignment(Qt::AlignRight); + m_form_label_map.insert(m_type_label, ""); +@@ -262,6 +264,7 @@ FilePreviewPage::FilePreviewPage(QWidget *parent) : QFrame(parent) + addSeparator(); + + m_total_size_label = new QLabel(this); ++ this->setLabelProperties(m_total_size_label); + m_form->addRow(tr("Size:"), m_total_size_label); + m_total_size_label->setAlignment(Qt::AlignRight); + m_form_label_map.insert(m_total_size_label, ""); +@@ -269,6 +272,7 @@ FilePreviewPage::FilePreviewPage(QWidget *parent) : QFrame(parent) + addSeparator(); + + m_time_create_label = new QLabel(this); ++ this->setLabelProperties(m_time_create_label); + m_form->addRow(tr("Time Created:"), m_time_create_label); + m_time_create_label->setAlignment(Qt::AlignRight); + m_form_label_map.insert(m_time_create_label, ""); +@@ -276,6 +280,7 @@ FilePreviewPage::FilePreviewPage(QWidget *parent) : QFrame(parent) + addSeparator(); + + m_time_modified_label = new QLabel(this); ++ this->setLabelProperties(m_time_modified_label); + m_form->addRow(tr("Time Modified:"), m_time_modified_label); + m_time_modified_label->setAlignment(Qt::AlignRight); + m_form_label_map.insert(m_time_modified_label, ""); +@@ -283,6 +288,7 @@ FilePreviewPage::FilePreviewPage(QWidget *parent) : QFrame(parent) + addSeparator(); + + m_time_access_label = new QLabel(this); ++ this->setLabelProperties(m_time_access_label); + m_form->addRow(tr("Time Access:"), m_time_access_label); + m_time_access_label->setAlignment(Qt::AlignRight); + m_form_label_map.insert(m_time_access_label, ""); +@@ -290,6 +296,7 @@ FilePreviewPage::FilePreviewPage(QWidget *parent) : QFrame(parent) + addSeparator(); + + m_file_count_label = new QLabel(this); ++ this->setLabelProperties(m_file_count_label); + QLabel *children_label = new QLabel(this); + children_label->setAlignment(Qt::AlignTop); + children_label->setText(tr("Children Count:")); +@@ -301,6 +308,7 @@ FilePreviewPage::FilePreviewPage(QWidget *parent) : QFrame(parent) + + //image + m_image_size = new QLabel(this); ++ this->setLabelProperties(m_image_size); + m_form->addRow(tr("Image resolution:"), m_image_size); + m_image_size->setAlignment(Qt::AlignRight); + m_form_label_map.insert(m_image_size, ""); +@@ -308,6 +316,7 @@ FilePreviewPage::FilePreviewPage(QWidget *parent) : QFrame(parent) + addSeparator(); + + m_image_format = new QLabel(this); ++ this->setLabelProperties(m_image_format); + m_form->addRow(tr("color model:"), m_image_format); + m_image_format->setAlignment(Qt::AlignRight); + m_form_label_map.insert(m_image_format, ""); +@@ -587,3 +596,9 @@ void FilePreviewPage::addSeparator() + separator->setFrameShape(QFrame::HLine); + m_form->addRow(separator); + } ++ ++void FilePreviewPage::setLabelProperties(QLabel *label) ++{ ++ label->setTextInteractionFlags(Qt::TextSelectableByMouse); ++ label->setCursor(Qt::IBeamCursor); ++} +diff --git a/libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.h b/libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.h +index b27a15d..79ece6c 100644 +--- a/libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.h ++++ b/libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.h +@@ -95,6 +95,7 @@ public: + void wrapData(QLabel *p_label, const QString &text); + void updateForm(QSize size); + void addSeparator(); ++ void setLabelProperties(QLabel *label); + + private Q_SLOTS: + void updateInfo(FileInfo *info); +diff --git a/libpeony-qt/controls/property-page/basic-properties-page.cpp b/libpeony-qt/controls/property-page/basic-properties-page.cpp +index d01dc60..971c3ee 100644 +--- a/libpeony-qt/controls/property-page/basic-properties-page.cpp ++++ b/libpeony-qt/controls/property-page/basic-properties-page.cpp +@@ -49,6 +49,7 @@ + #include <QtConcurrent> + #include <QGSettings> + #include <QComboBox> ++#include <QButtonGroup> + + #include "file-info.h" + #include "file-info-job.h" +@@ -63,6 +64,8 @@ + #include "generic-thumbnailer.h" + #include "file-operation-manager.h" + #include "open-with-properties-page.h" ++#include "file-enumerator.h" ++#include "file-properties-operation.h" + + #include <QApplication> + +@@ -207,10 +210,7 @@ void BasicPropertiesPage::loadData() + + this->loadPartOne(); + this->loadPartTwo(); +- +- if (m_fileType != BP_MultipleFIle) { +- this->loadOptionalData(); +- } ++ this->loadOptionalData(); + } + + void BasicPropertiesPage::initFloorOne() +@@ -317,21 +317,25 @@ void BasicPropertiesPage::initFloorTwo() + default: + break; + } ++ } + +- //===只读和隐藏选择框=== +- m_readOnly = new QCheckBox(tr("Readonly"), baseFrame); +- m_hidden = new QCheckBox(tr("Hidden"), baseFrame); +- +- QHBoxLayout *checkboxLayout = new QHBoxLayout(baseFrame); +- checkboxLayout->addSpacing(1); +- checkboxLayout->addWidget(m_readOnly, Qt::AlignLeft); +- checkboxLayout->addSpacing(35); +- checkboxLayout->addWidget(m_hidden, Qt::AlignLeft); +- checkboxLayout->addStretch(1); ++ //===只读和隐藏选择框=== ++ m_readOnly = new QCheckBox(tr("Readonly"), baseFrame); ++ m_hidden = new QCheckBox(tr("Hidden"), baseFrame); + +- baseLayout->addRow(tr("Property:"), checkboxLayout); ++ if (m_info->isDir() && m_uris.count() == 1) { ++ m_readOnly->setText(tr("Readonly (just applied by subfiles)")); + } + ++ QHBoxLayout *checkboxLayout = new QHBoxLayout(baseFrame); ++ checkboxLayout->addSpacing(1); ++ checkboxLayout->addWidget(m_readOnly, Qt::AlignLeft); ++ checkboxLayout->addSpacing(35); ++ checkboxLayout->addWidget(m_hidden, Qt::AlignLeft); ++ checkboxLayout->addStretch(1); ++ ++ baseLayout->addRow(tr("Property:"), checkboxLayout); ++ + m_layout->addWidget(baseFrame); + } + +@@ -484,49 +488,73 @@ void BasicPropertiesPage::loadPartTwo() + + void BasicPropertiesPage::loadOptionalData() + { +- this->setSysTimeFormat(); +- // set time +- if (QGSettings::isSchemaInstalled("org.ukui.control-center.panel.plugins")) { +- QGSettings* settings = new QGSettings("org.ukui.control-center.panel.plugins", QByteArray(), this); +- connect(settings, &QGSettings::changed, this, [=](const QString &key) { +- if ("hoursystem" == key) { +- setSysTimeFormat(); +- updateInfo(m_info->uri()); +- } +- }); +- } ++ if (m_fileType != BP_MultipleFIle) { ++ this->setSysTimeFormat(); ++ // set time ++ if (QGSettings::isSchemaInstalled("org.ukui.control-center.panel.plugins")) { ++ QGSettings* settings = new QGSettings("org.ukui.control-center.panel.plugins", QByteArray(), this); ++ connect(settings, &QGSettings::changed, this, [=](const QString &key) { ++ if ("hoursystem" == key) { ++ setSysTimeFormat(); ++ updateInfo(m_info->uri()); ++ } ++ }); ++ } + + #ifdef KY_SDK_DATE +- connect(GlobalSettings::getInstance(), +- &GlobalSettings::updateLongDataFormat, +- this, +- &BasicPropertiesPage::updateDateFormat); ++ connect(GlobalSettings::getInstance(), ++ &GlobalSettings::updateLongDataFormat, ++ this, ++ &BasicPropertiesPage::updateDateFormat); + #endif + +- updateInfo(m_info.get()->uri()); +- connect(m_watcher.get(), &FileWatcher::locationChanged, [=](const QString&, const QString &uri) { +- this->updateInfo(uri); +- }); +- +- //底部隐藏多选框和只读选择框 +- if(m_info.get()->canRead() && !m_info.get()->canWrite()) +- m_readOnly->setCheckState(Qt::Checked); ++ updateInfo(m_info.get()->uri()); ++ connect(m_watcher.get(), &FileWatcher::locationChanged, [=](const QString&, const QString &uri) { ++ this->updateInfo(uri); ++ }); ++ } + +- if(m_info.get()->displayName().startsWith(".")) +- m_hidden->setCheckState(Qt::Checked); ++ if (m_fileType != BP_MultipleFIle && m_fileType != BP_Folder) { ++ bool isHidden = m_info.get()->property(G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN).toBool(); ++ if(m_info.get()->canRead() && !m_info.get()->canWrite()) { ++ m_readOnly->setCheckState(Qt::Checked); + +- m_readOnly->setDisabled(!m_info->canRename()); ++ if(m_info.get()->displayName().startsWith(".") || isHidden) ++ m_hidden->setCheckState(Qt::Checked); + +- QString desktopPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); +- bool isDesktop = FileUtils::isSamePath(m_info->uri(), desktopPath); +- //fix bug#113890,hiden Desktop folder change desktop show +- m_hidden->setDisabled(!m_info->canRename() || isDesktop); +- m_isReadOnly = m_readOnly->isChecked(); ++ m_readOnly->setDisabled(!m_info->canRename()); + ++ QString desktopPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); ++ bool isDesktop = FileUtils::isSamePath(m_info->uri(), desktopPath); ++ //fix bug#113890,hiden Desktop folder change desktop show ++ m_hidden->setDisabled(!m_info->canRename() || isDesktop); ++ m_isReadOnly = m_readOnly->isChecked(); ++ } ++ } else { ++ BatchStatusThread *batchStatusThread = new BatchStatusThread(m_uris); ++ batchStatusThread->start(); ++ connect(batchStatusThread, &BatchStatusThread::updateState, this, [=](Qt::CheckState readOnlyState, Qt::CheckState hiddenState){ ++ if(m_fileType == BP_Folder){/* 单选一个文件夹的场景,参照windows 只读复选框显示为部分勾选状态;link to bug#265731 一级目录设置只读后再次打开属性界面,只读为未勾选状态 */ ++ readOnlyState = Qt::CheckState::PartiallyChecked; ++ }//end ++ m_readOnly->setCheckState(readOnlyState); ++ m_hidden->setCheckState(hiddenState); ++ m_readOnlyState = readOnlyState; ++ m_hiddenState = hiddenState; ++ }); ++ connect(batchStatusThread, &BatchStatusThread::updateDisabled, this, [=](const bool &readOnlyDisable, const bool &hiddenDisable){ ++ m_readOnly->setDisabled(readOnlyDisable); ++ m_hidden->setDisabled(hiddenDisable); ++ }); ++ connect(batchStatusThread, &BatchStatusThread::updateIsAllDir, this, [=](const bool &isAllDir){ ++ m_isAllDir = isAllDir; ++ }); ++ connect(batchStatusThread, &BatchStatusThread::finished, batchStatusThread, &BatchStatusThread::deleteLater); ++ } + + //确认被修改 +- connect(m_readOnly,&QCheckBox::stateChanged,this,&BasicPropertiesPage::thisPageChanged); +- connect(m_hidden,&QCheckBox::stateChanged,this,&BasicPropertiesPage::thisPageChanged); ++ connect(m_readOnly, &QCheckBox::stateChanged, this, &BasicPropertiesPage::thisPageChanged); ++ connect(m_hidden, &QCheckBox::stateChanged, this, &BasicPropertiesPage::thisPageChanged); + } + + BasicPropertiesPage::FileType BasicPropertiesPage::checkFileType(const QStringList &uris) +@@ -811,103 +839,160 @@ void BasicPropertiesPage::saveAllChange() + if (!this->m_thisPageChanged) + return; + +- //拒绝修改home目录 +- if (m_info.get()->uri() == ("file://"+QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first())) { +- return; +- } +- //修改图标 +- this->changeFileIcon(); +- +- if (m_readOnly && m_isReadOnly != m_readOnly->isChecked()) { +- mode_t mod = 0; +- quint32 mode = 0; +- if(m_readOnly->isChecked()) { +-// mod |= S_IRUSR; +-// mod |= S_IRGRP; +-// mod |= S_IROTH; +- +- g_autoptr(GFile) file = g_file_new_for_uri(m_info.get()->uri().toUtf8().constData()); +- if (file) { +- g_autoptr(GError) error = NULL; +- g_autoptr(GFileInfo) info = g_file_query_info(file, +- "unix::mode", +- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, +- nullptr, +- &error); +- bool has_unix_mode = g_file_info_has_attribute(info, G_FILE_ATTRIBUTE_UNIX_MODE); +- if (has_unix_mode) { +- mode = g_file_info_get_attribute_uint32(info, G_FILE_ATTRIBUTE_UNIX_MODE); +- auto metaInfo = FileMetaInfo::fromUri(m_info.get()->uri()); +- if (metaInfo) { +- metaInfo->setMetaInfoInt(TEMP_PERMISSIONS, mode); +- } ++ if (BP_MultipleFIle != this->checkFileType(m_uris)) { ++ //拒绝修改home目录 ++ if (m_info.get()->uri() == ("file://"+QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first())) { ++ return; ++ } ++ //修改图标 ++ this->changeFileIcon(); ++ ++ if (m_info->isDir()) { ++ if ((m_readOnly && m_readOnlyState != m_readOnly->checkState()) ++ || (m_hidden && m_hiddenState != m_hidden->checkState())) { ++ bool isReadOnly = false; ++ bool isHidden = false; ++ ++ if (Qt::Checked == m_readOnly->checkState()) { ++ isReadOnly = true; + } ++ if (Qt::Checked == m_hidden->checkState()) { ++ isHidden = true; ++ } ++ ++ PropertiesSetDialog *dialog = new PropertiesSetDialog(); ++ connect(dialog, &PropertiesSetDialog::sendSelectRadioButton, this, [=](int id){ ++ //id == 0 设置当前所选项 id == 1 递归设置 ++ bool isRecursive = false; ++ if (id) { ++ isRecursive = true; ++ } ++ FileOperationUtils::setReadOnlyAndHidden(QStringList() << m_info->uri(), isReadOnly, isHidden, isRecursive); ++ }); ++ dialog->exec(); + } +- //去除写权限 +- mode &= ~S_IWUSR; +- mode &= ~S_IWGRP; +- mode &= ~S_IWOTH; +- mod = mode; + } else { +- auto metaInfo = FileMetaInfo::fromUri(m_info.get()->uri()); +- if (metaInfo && metaInfo->getMetaInfoInt(TEMP_PERMISSIONS)) { +- mod = metaInfo->getMetaInfoInt(TEMP_PERMISSIONS); +- } else { +- mod |= S_IRUSR; +- mod |= S_IRGRP; +- mod |= S_IROTH; ++ if (m_readOnly && m_isReadOnly != m_readOnly->isChecked()) { ++ mode_t mod = 0; ++ quint32 mode = 0; ++ g_autoptr(GFile) file = g_file_new_for_uri(m_info.get()->uri().toUtf8().constData()); ++ if(m_readOnly->isChecked()) { ++ // mod |= S_IRUSR; ++ // mod |= S_IRGRP; ++ // mod |= S_IROTH; ++ if (file) { ++ g_autoptr(GError) error = NULL; ++ g_autoptr(GFileInfo) info = g_file_query_info(file, ++ "unix::mode", ++ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, ++ nullptr, ++ &error); ++ bool has_unix_mode = g_file_info_has_attribute(info, G_FILE_ATTRIBUTE_UNIX_MODE); ++ if (has_unix_mode) { ++ mode = g_file_info_get_attribute_uint32(info, G_FILE_ATTRIBUTE_UNIX_MODE); ++ auto metaInfo = FileMetaInfo::fromUri(m_info.get()->uri()); ++ if (metaInfo) { ++ metaInfo->setMetaInfoInt(TEMP_PERMISSIONS, mode); ++ } ++ } ++ } ++ //去除写权限 ++ mode &= ~S_IWUSR; ++ mode &= ~S_IWGRP; ++ mode &= ~S_IWOTH; ++ mod = mode; ++ } else { ++ auto metaInfo = FileMetaInfo::fromUri(m_info.get()->uri()); ++ if (metaInfo && metaInfo->getMetaInfoInt(TEMP_PERMISSIONS)) { ++ mod = metaInfo->getMetaInfoInt(TEMP_PERMISSIONS); ++ } else { ++ mod |= S_IRUSR; ++ mod |= S_IRGRP; ++ mod |= S_IROTH; ++ ++ mod |= S_IWUSR; ++ // mod |= S_IWGRP; ++ // mod |= S_IWOTH; ++ } ++ } ++ //FIX:如果该文件之前就是可执行,那么应该保留可执行权限 ++ if (m_info->canExecute()) ++ mod |= S_IXUSR; ++ ++ //.desktop文件给予可执行,.desktop文件原本可执行才给可执行权限 ++ if (((m_info.get()->isDesktopFile()) || m_info.get()->displayName().endsWith(".desktop")) && m_info->canExecute()) { ++ //FIX:可执行范围 目前只给拥有者执行权限 ++ mod |= S_IXUSR; ++ //mod |= S_IXGRP; ++ //mod |= S_IXOTH; ++ } ++// QUrl url = m_info.get()->uri(); ++// g_chmod(url.path().toUtf8(), mod); ++ g_file_set_attribute_uint32(file, G_FILE_ATTRIBUTE_UNIX_MODE, (guint32)mod, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); + +- mod |= S_IWUSR; +-// mod |= S_IWGRP; +-// mod |= S_IWOTH; + } +- } +- //FIX:如果该文件之前就是可执行,那么应该保留可执行权限 +- if (m_info->canExecute()) +- mod |= S_IXUSR; +- +- //.desktop文件给予可执行,.desktop文件原本可执行才给可执行权限 +- if (((m_info.get()->isDesktopFile()) || m_info.get()->displayName().endsWith(".desktop")) && m_info->canExecute()) { +- //FIX:可执行范围 目前只给拥有者执行权限 +- mod |= S_IXUSR; +- //mod |= S_IXGRP; +- //mod |= S_IXOTH; +- } +- QUrl url = m_info.get()->uri(); +- g_chmod(url.path().toUtf8(), mod); + +- } ++ //是否进行文件隐藏操作 - Whether to hide files ++ bool existHiddenOpt = false; ++ if (m_hidden) { ++ QString newName = m_info.get()->displayName(); + +- //是否进行文件隐藏操作 - Whether to hide files +- bool existHiddenOpt = false; +- if (m_hidden) { +- QString newName = m_info.get()->displayName(); ++ if (newName.startsWith(".")) ++ newName = newName.mid(1,-1); + +- if (newName.startsWith(".")) +- newName = newName.mid(1,-1); ++ if (isNameChanged()) { ++ newName = m_displayNameEdit->text(); ++ } + +- if (isNameChanged()) { +- newName = m_displayNameEdit->text(); +- } ++ bool isHidden = m_info.get()->displayName().startsWith("."); + +- bool isHidden = m_info.get()->displayName().startsWith("."); ++ //以前没隐藏,并且选中隐藏框 ++ if(!isHidden && m_hidden->isChecked()) { ++ newName = "." + newName; ++ FileOperationUtils::rename(m_info.get()->uri(), newName, true); ++ existHiddenOpt = true; + +- //以前没隐藏,并且选中隐藏框 +- if(!isHidden && m_hidden->isChecked()) { +- newName = "." + newName; +- FileOperationUtils::rename(m_info.get()->uri(), newName, true); +- existHiddenOpt = true; ++ } else if(isHidden && !m_hidden->isChecked()) { ++ //以前已经隐藏,并且取消选中隐藏框 ++ FileOperationUtils::rename(m_info.get()->uri(), newName, true); ++ existHiddenOpt = true; ++ } ++ } + +- } else if(isHidden && !m_hidden->isChecked()) { +- //以前已经隐藏,并且取消选中隐藏框 +- FileOperationUtils::rename(m_info.get()->uri(), newName, true); +- existHiddenOpt = true; ++ if (!existHiddenOpt) { ++ if (isNameChanged()) { ++ FileOperationUtils::rename(m_info.get()->uri(), m_displayNameEdit->text(), true); ++ } ++ } + } +- } ++ } else { ++ if ((m_readOnly && m_readOnlyState != m_readOnly->checkState()) ++ || (m_hidden && m_hiddenState != m_hidden->checkState())) { + +- if (!existHiddenOpt) { +- if (isNameChanged()) { +- FileOperationUtils::rename(m_info.get()->uri(), m_displayNameEdit->text(), true); ++ bool isReadOnly = false; ++ bool isHidden = false; ++ ++ if (Qt::Checked == m_readOnly->checkState()) { ++ isReadOnly = true; ++ } ++ if (Qt::Checked == m_hidden->checkState()) { ++ isHidden = true; ++ } ++ ++ if (m_isAllDir) { ++ PropertiesSetDialog *dialog = new PropertiesSetDialog(); ++ connect(dialog, &PropertiesSetDialog::sendSelectRadioButton, this, [=](int id){ ++ //id == 0 设置当前所选项 id == 1 递归设置 ++ bool isRecursive = false; ++ if (id) { ++ isRecursive = true; ++ } ++ FileOperationUtils::setReadOnlyAndHidden(m_uris, isReadOnly, isHidden, isRecursive); ++ }); ++ dialog->exec(); ++ } else { ++ FileOperationUtils::setReadOnlyAndHidden(m_uris, isReadOnly, isHidden, false); ++ } + } + } + +@@ -1252,7 +1337,7 @@ void FileNameThread::run() + } + } else { + QStringList stringList; +- for (auto uri : m_uris) { ++ for (const auto &uri : m_uris) { + //FIXME: replace BLOCKING api in ui thread.(finish) ** + std::shared_ptr<FileInfo> fileInfo = FileInfo::fromUri(uri); + FileInfoJob *fileInfoJob = new FileInfoJob(fileInfo); +@@ -1266,3 +1351,178 @@ void FileNameThread::run() + + Q_EMIT fileNameReady(fileName); + } ++ ++BatchStatusThread::BatchStatusThread(const QStringList &uris) ++ : m_uris(uris) ++{ ++ qRegisterMetaType<Qt::CheckState>("Qt::CheckState"); ++} ++ ++void BatchStatusThread::run() ++{ ++ for (const auto &uri : m_uris) { ++ QStringList stringList; ++ FileEnumerator e; ++ e.setEnumerateDirectory(uri); ++ e.enumerateSync(); ++ stringList.append(e.getChildrenUris()); ++ ++ for (auto u : stringList) { ++ queryInfoUpdate(u, false, false); ++ } ++ queryInfoUpdate(uri, true, true); ++ } ++ ++ Qt::CheckState readOnlyState = Qt::CheckState::Unchecked; ++ Qt::CheckState hiddenState = Qt::CheckState::Unchecked; ++ if (m_existReadOnly && !m_existWrite) { ++ readOnlyState = Qt::CheckState::Checked; ++ } else if (m_existReadOnly && m_existWrite) { ++ readOnlyState = Qt::CheckState::PartiallyChecked; ++ } else { ++ readOnlyState = Qt::CheckState::Unchecked; ++ } ++ ++ if (m_existHidden && !m_existShow) { ++ hiddenState = Qt::CheckState::Checked; ++ } else if (m_existHidden && m_existShow) { ++ hiddenState = Qt::CheckState::PartiallyChecked; ++ } else { ++ hiddenState = Qt::CheckState::Unchecked; ++ } ++ ++ bool hiddenDisable = !m_canRename || m_isDesktop; ++ ++ Q_EMIT updateState(readOnlyState, hiddenState); ++ Q_EMIT updateDisabled(!m_canRename, hiddenDisable); ++ Q_EMIT updateIsAllDir(m_isAllDir); ++} ++ ++void BatchStatusThread::queryInfoUpdate(const QString &uri, const bool &queryHidden, const bool &queryIsAllDir) ++{ ++ QString desktopPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); ++ bool canRead = false; ++ bool canWrite = false; ++ g_autoptr(GFile) file = g_file_new_for_uri(uri.toUtf8().constData()); ++ g_autoptr(GFileInfo) fileInfo = g_file_query_info(file, ++ "standard::*," "access::*,", ++ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, ++ nullptr, ++ nullptr); ++ ++ if (g_file_info_has_attribute(fileInfo, G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) { ++ canRead = g_file_info_get_attribute_boolean(fileInfo, G_FILE_ATTRIBUTE_ACCESS_CAN_READ); ++ } else { ++ canRead = true; ++ } ++ ++ if (g_file_info_has_attribute(fileInfo, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) { ++ canWrite = g_file_info_get_attribute_boolean(fileInfo, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE); ++ } else { ++ canWrite = true; ++ } ++ ++ if (!g_file_info_get_attribute_boolean(fileInfo, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME)) { ++ m_canRename = false; ++ } ++ ++ if (queryHidden) { ++ if (g_file_info_get_attribute_boolean(fileInfo, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN)) { ++ m_existHidden = true; ++ } else { ++ m_existShow = true; ++ } ++ } ++ ++ if (queryIsAllDir) { ++ GFileType fileType = g_file_info_get_file_type(fileInfo); ++ QString contentType = g_file_info_get_content_type (fileInfo); ++ if (fileType == G_FILE_TYPE_DIRECTORY || contentType == "inode/directory") { ++ m_isAllDir = true; ++ } ++ } ++ ++ bool isDesktop = FileUtils::isSamePath(uri, desktopPath); ++ if (isDesktop) { ++ m_isDesktop = isDesktop; ++ } ++ ++ if(canRead && !canWrite) { ++ m_existReadOnly = true; ++ } else { ++ m_existWrite = true; ++ } ++} ++ ++PropertiesSetDialog::PropertiesSetDialog(QWidget *parent) ++ : QDialog(parent) ++{ ++ this->initUI(); ++ connect(m_okBtn, &QPushButton::clicked, this, [=](){ ++ int id = m_group->checkedId(); ++ Q_EMIT sendSelectRadioButton(id); ++ accept(); ++ }); ++ connect(m_cancelBtn, &QPushButton::clicked, this, &PropertiesSetDialog::reject); ++} ++ ++PropertiesSetDialog::~PropertiesSetDialog() ++{ ++ ++} ++ ++void PropertiesSetDialog::initUI() ++{ ++ this->setAutoFillBackground(true); ++ this->setWindowTitle(tr("Confirming property settings")); ++ this->setFixedSize(460, 300); ++ this->setContentsMargins(0, 0, 0, 0); ++ ++ m_layout = new QVBoxLayout(this); ++ m_layout->setMargin(0); ++ m_layout->setSpacing(0); ++ ++ m_label = new QLabel(tr("Whether to apply to the current selected option or to selected options and subfolders and subfiles.")); ++ m_label->setContentsMargins(22, 10, 22, 0); ++ m_label->setWordWrap(true); ++ m_label->setAutoFillBackground(true); ++ m_layout->addWidget(m_label); ++ m_layout->addStretch(1); ++ ++ QGridLayout *gridLayout = new QGridLayout; ++ gridLayout->setContentsMargins(22, 0, 22, 0); ++ gridLayout->setSpacing(0); ++ ++ QLabel *currentSelectLabel = new QLabel(tr("Apply the current selection"), this); ++ QLabel *recursiveLabel = new QLabel(tr("Apply the current selection as well as subfolders and subfiles"), this); ++ currentSelectLabel->setWordWrap(true); ++ recursiveLabel->setWordWrap(true); ++ ++ m_currentSelectBtn = new QRadioButton(this); ++ m_recursiveBtn = new QRadioButton(this); ++ m_recursiveBtn->setChecked(true); ++ ++ m_group = new QButtonGroup; ++ m_group->setExclusive(true); ++ m_group->addButton(m_currentSelectBtn, 0); ++ m_group->addButton(m_recursiveBtn, 1); ++ gridLayout->addWidget(m_currentSelectBtn, 0, 0, 1, 1); ++ gridLayout->addWidget(currentSelectLabel, 0, 1, 1, 15); ++ gridLayout->addWidget(m_recursiveBtn, 1, 0, 1, 1); ++ gridLayout->addWidget(recursiveLabel, 1, 1, 1, 15); ++ m_layout->addLayout(gridLayout); ++ m_layout->addSpacing(10); ++ m_layout->addStretch(1); ++ ++ QHBoxLayout *hBoxLayout = new QHBoxLayout; ++ hBoxLayout->setContentsMargins(22, 0, 22, 0); ++ hBoxLayout->setSpacing(0); ++ m_okBtn = new QPushButton(tr("Ok")); ++ m_cancelBtn = new QPushButton(tr("Cancel")); ++ hBoxLayout->addStretch(1); ++ hBoxLayout->addWidget(m_cancelBtn); ++ hBoxLayout->addSpacing(10); ++ hBoxLayout->addWidget(m_okBtn); ++ m_layout->addLayout(hBoxLayout); ++ m_layout->addSpacing(16); ++} +diff --git a/libpeony-qt/controls/property-page/basic-properties-page.h b/libpeony-qt/controls/property-page/basic-properties-page.h +index f96a8e8..57aa282 100644 +--- a/libpeony-qt/controls/property-page/basic-properties-page.h ++++ b/libpeony-qt/controls/property-page/basic-properties-page.h +@@ -34,6 +34,7 @@ + #include <QProxyStyle> + #include <QComboBox> + #include <QDBusInterface> ++#include <QRadioButton> + + #include "properties-window-tab-iface.h" + #include "open-with-properties-page.h" +@@ -57,10 +58,55 @@ class FileInfo; + class FileWatcher; + class FileCountOperation; + ++class PropertiesSetDialog : public QDialog { ++ Q_OBJECT ++public: ++ explicit PropertiesSetDialog(QWidget *parent = nullptr); ++ ~PropertiesSetDialog(); ++ void initUI(); ++ ++Q_SIGNALS: ++ void sendSelectRadioButton(int Id); ++ ++private: ++ QVBoxLayout *m_layout = nullptr; ++ QLabel *m_label = nullptr; ++ QRadioButton *m_currentSelectBtn = nullptr; ++ QRadioButton *m_recursiveBtn = nullptr; ++ QPushButton *m_okBtn = nullptr; ++ QPushButton *m_cancelBtn = nullptr; ++ QButtonGroup *m_group = nullptr; ++}; ++ ++class BatchStatusThread : public QThread { ++ Q_OBJECT ++public: ++ BatchStatusThread(const QStringList &uris); ++ ++protected: ++ void run(); ++ void queryInfoUpdate(const QString &uri, const bool &queryHidden = false, const bool &queryIsAllDir = false); ++ ++Q_SIGNALS: ++ void updateState(Qt::CheckState readOnlyState, Qt::CheckState hiddenState); ++ void updateDisabled(const bool &readOnlyDisable, const bool &hiddenDisable); ++ void updateIsAllDir(const bool &isAllDir); ++ ++private: ++ const QStringList m_uris; ++ bool m_existReadOnly = false; ++ bool m_existWrite = false; ++ bool m_existHidden = false; ++ bool m_existShow = false; ++ bool m_canRename = true; ++ bool m_isDesktop = false; ++ bool m_isAllDir = false; ++}; ++ + class FileNameThread : public QThread { + Q_OBJECT + private: +- const QStringList &m_uris; ++ const QStringList m_uris; + public: + FileNameThread(const QStringList &uris) : m_uris(uris){} + +@@ -129,6 +175,7 @@ protected: + + void updateCountInfo(bool isDone = false); + void addOpenWithLayout(QWidget *parent = nullptr); ++ + BasicPropertiesPage::FileType checkFileType(const QStringList &uris); + void chooseFileIcon(); + void changeFileIcon(); +@@ -207,6 +254,9 @@ private: + + QString m_date_format = ""; + QDBusInterface *mDbusDateServer; ++ Qt::CheckState m_readOnlyState; ++ Qt::CheckState m_hiddenState; ++ bool m_isAllDir = false; + }; + + class PushButtonStyle : public QProxyStyle +diff --git a/libpeony-qt/controls/property-page/computer-properties-page.cpp b/libpeony-qt/controls/property-page/computer-properties-page.cpp +index 93ea9df..e345c03 100644 +--- a/libpeony-qt/controls/property-page/computer-properties-page.cpp ++++ b/libpeony-qt/controls/property-page/computer-properties-page.cpp +@@ -25,6 +25,9 @@ + #include "linux-pwd-helper.h" + + #include "file-utils.h" ++#include "file-info.h" ++#include "file-enumerator.h" ++#include "file-count-operation.h" + + #ifndef KY_UDF_BURN + #include "datacdrom.h" +@@ -44,8 +47,10 @@ using namespace UdfBurn; + #include <QProcess> + #include <QDBusConnection> + #include <QDBusInterface> ++#include <QTimer> + + #include <glib.h> ++#include <gio/gdesktopappinfo.h> + + using namespace Peony; + +@@ -139,9 +144,140 @@ ComputerPropertiesPage::ComputerPropertiesPage(const QString &uri, QWidget *pare + if (type.contains("ext")) { + used = total - available; + } +- m_layout->addRow(tr("Name: "), new QLabel(targetUri == "file:///" ? tr("File System") : tr("Data"), this)); ++ m_layout->addRow(tr("Name: "), new QLabel(targetUri == "file:///" ? tr("System Disk") : tr("Data"), this)); + m_layout->addRow(tr("Total Space: "), new QLabel(formatCapacityString(total), this)); + m_layout->addRow(tr("Used Space: "), new QLabel(formatCapacityString(used), this)); ++ ++ //story 28545, improve data block solution ++ if (targetUri == "file:///data" && Peony::FileUtils::isFileExsit("file:///data/usershare") && ++ Peony::FileUtils::isFileExsit("file:///data/root") && Peony::FileUtils::isFileExsit("file:///data/home")) { ++ QLabel* rootUsedLabel = new QLabel(tr("In calculation..."), this); ++ QLabel* homeUsedLabel = new QLabel(tr("In calculation..."), this); ++ QLabel* usershareLabel = new QLabel(tr("In calculation..."), this); ++ m_layout->addRow(tr("/root used: "), rootUsedLabel); ++ m_layout->addRow(tr("/home used: "), homeUsedLabel); ++ m_layout->addRow(tr("/usershare used: "), usershareLabel); ++ ++ QLabel* othersLabel = new QLabel(tr("In calculation..."), this); ++ othersLabel->setVisible(false); ++ if (FileUtils::isDataBlockHasUserFile()) { ++ othersLabel->setVisible(true); ++ m_layout->addRow(tr("/data/* used: "), othersLabel); ++ } ++ ++ m_timer = new QTimer(this); ++ m_timer->setInterval(500); ++ connect(m_timer, &QTimer::timeout, this, [=]{ ++ homeUsedLabel->setText(formatCapacityString(m_home_counted_size)); ++ usershareLabel->setText(formatCapacityString(m_usershare_counted_size)); ++ if (FileUtils::isDataBlockHasUserFile()) { ++ othersLabel->setText(formatCapacityString(m_others_counted_size)); ++ }else { ++ m_count_others_done = true; ++ } ++ ++ if (m_count_home_done && m_count_usershare_done && m_count_others_done) { ++ m_timer->stop(); ++ if (used > (m_home_counted_size + m_root_counted_size + m_others_counted_size)) { ++ rootUsedLabel->setText(formatCapacityString(used - (m_home_counted_size + m_root_counted_size + m_others_counted_size))); ++ }else { ++ rootUsedLabel->setText(tr("Unknow (No permission)")); ++ } ++ } ++ }); ++ m_timer->start(); ++ ++ m_home_counted_size = 0; ++ m_root_counted_size = 0; ++ m_usershare_counted_size = 0; ++ m_others_counted_size = 0; ++ m_count_home_done = false; ++ m_count_usershare_done = false; ++ m_count_others_done = false; ++ ++ //计算家目录占用空间 ++ FileCountOperation *homeCountOp = new FileCountOperation(QStringList() << "file:///home", false); ++ connect(homeCountOp, &FileOperation::operationPreparedOne, this, [=](QString uri, quint64 countSize) { ++ m_home_counted_size += countSize; ++ }, Qt::BlockingQueuedConnection); ++ connect(homeCountOp, &FileCountOperation::countDone, this, [=](quint64 file_count, quint64 hidden_file_count, quint64 total_size) { ++ homeUsedLabel->setText(formatCapacityString(total_size)); ++ m_count_home_done = true; ++ }, Qt::BlockingQueuedConnection); ++ ++ //计算useshare目录占用空间 ++ FileCountOperation *useshareCountOp = new FileCountOperation(QStringList() << "file:///data/usershare", false); ++ connect(useshareCountOp, &FileOperation::operationPreparedOne, this, [=](QString uri, quint64 countSize) { ++ m_usershare_counted_size += countSize; ++ }, Qt::BlockingQueuedConnection); ++ connect(useshareCountOp, &FileCountOperation::countDone, this, [=](quint64 file_count, quint64 hidden_file_count, quint64 total_size) { ++ usershareLabel->setText(formatCapacityString(total_size)); ++ m_count_usershare_done = true; ++ }, Qt::BlockingQueuedConnection); ++ ++ //计算除了useshare,home, root之外的目录占用空间 ++ FileCountOperation *othersCountOp = new FileCountOperation(QStringList() << "file:///data", false); ++ QStringList systemUris; ++ systemUris<< "file:///data/root"<< "file:///data/home" <<"file:///data/usershare" << "file:///root" <<"file:///home"; ++ othersCountOp->setSkipUris(systemUris); ++ connect(othersCountOp, &FileOperation::operationPreparedOne, this, [=](QString uri, quint64 countSize) { ++ m_others_counted_size += countSize; ++ }, Qt::BlockingQueuedConnection); ++ connect(othersCountOp, &FileCountOperation::countDone, this, [=](quint64 file_count, quint64 hidden_file_count, quint64 total_size) { ++ if (FileUtils::isDataBlockHasUserFile()) { ++ othersLabel->setText(formatCapacityString(m_others_counted_size)); ++ }else { ++ m_others_counted_size = 0; ++ } ++ m_count_others_done = true; ++ }, Qt::BlockingQueuedConnection); ++ ++ QThreadPool::globalInstance()->start(homeCountOp); ++ QThreadPool::globalInstance()->start(useshareCountOp); ++ QThreadPool::globalInstance()->start(othersCountOp); ++ ++ ++ //阻塞方式,文件数量大场景阻塞,替换为异步方式 ++// QTimer::singleShot(500, this, [=]() { ++// quint64 homeUsed = FileUtils::getFileTotalSize("file:///home"); ++// quint64 usershareUsed = FileUtils::getFileTotalSize("file:///data/usershare"); ++// homeUsedLabel->setText(formatCapacityString(homeUsed)); ++// usershareLabel->setText(formatCapacityString(usershareUsed)); ++// quint64 rootUsed = 0; ++// if (used > (homeUsed + usershareUsed)) { ++// rootUsed = used - homeUsed -usershareUsed; ++// rootUsedLabel->setText(formatCapacityString(rootUsed)); ++// } ++// else ++// rootUsedLabel->setText(tr("Unknow (No permission)")); ++ ++// if (FileUtils::isDataBlockHasUserFile()) { ++// FileEnumerator e; ++// e.setEnumerateDirectory("file:///data"); ++// e.enumerateSync(); ++// QStringList systemUris; ++// systemUris<< "file:///data/root"<< "file:///data/home" <<"file:///data/usershare" << "file:///root" <<"file:///home"; ++ ++// quint64 others = 0; ++// for (auto fileInfo : e.getChildren()) { ++// QString childUri = fileInfo->uri(); ++// if (systemUris.contains(childUri)) ++// continue; ++ ++// others += FileUtils::getFileTotalSize(childUri); ++// } ++ ++// if (rootUsed > others) { ++// rootUsed -= others; ++// rootUsedLabel->setText(formatCapacityString(rootUsed)); ++// }else { ++// rootUsedLabel->setText(tr("Unknow (No permission)")); ++// } ++// othersLabel->setText(formatCapacityString(others)); ++// } ++// }); ++ } ++ + m_layout->addRow(tr("Free Space: "), new QLabel(formatCapacityString(available), this)); + m_layout->addRow(tr("Type: "), new QLabel(fs_type, this)); + +@@ -195,14 +331,23 @@ ComputerPropertiesPage::ComputerPropertiesPage(const QString &uri, QWidget *pare + //光盘 + if (!unixDeviceName.isNull() && !unixDeviceName.isEmpty() && unixDeviceName.startsWith("/dev/sr")) { + isCDDisk = true; +- DataCDROM *cdrom = new DataCDROM(unixDeviceName); ++#ifdef KY_UDF_BURN ++ UdfBurn::DataCDROM *cdrom = new UdfBurn::DataCDROM(unixDeviceName); ++#else ++ Peony::DataCDROM *cdrom = new Peony::DataCDROM(unixDeviceName); ++#endif + if (cdrom) { + cdrom->getCDROMInfo(); + //usedSpace = used; + //used无法正确获取追加刻录后光盘的使用容量,getCDROMUsedCapacity()无法获取可擦除光盘的使用容量 + usedSpace = cdrom->getCDROMUsedCapacity(); + if((cdrom->getCDROMType()).contains("DVD+RW") || (cdrom->getCDROMType()).contains("DVD-RW")){ +- usedSpace =used; ++ QString fsType = getFileSystemType(uri); ++ if(fsType.toLower().startsWith("iso")){/* "iso9660"文件系统类型"DVD+RW"和"DVD-RW"光盘的使用容量从libkyudfburn中获取,link bug#271455. */ ++ usedSpace = cdrom->getCDROMUsedCapacity(); ++ }else{ ++ usedSpace =used; ++ } + } + totalSpace = cdrom->getCDROMCapacity(); + availableSpace = totalSpace - usedSpace; +@@ -261,9 +406,13 @@ ComputerPropertiesPage::ComputerPropertiesPage(const QString &uri, QWidget *pare + if (isCDDisk && QFile::exists("/usr/bin/kylin-burner")) { + auto pushbutton = new QPushButton(tr("Kylin Burner")); + connect(pushbutton, &QPushButton::clicked, pushbutton, [=](){ +- QProcess p; +- p.startDetached("/usr/bin/kylin-burner"); +- p.waitForStarted(); ++ QString path = "/usr/share/applications/kylin-burner.desktop"; ++ g_autoptr(GDesktopAppInfo) appInfo = g_desktop_app_info_new_from_filename(path.toUtf8().constData()); ++ if (info) { ++ g_app_info_launch_uris_async(G_APP_INFO(appInfo), nullptr, ++ nullptr, nullptr, ++ nullptr, nullptr); ++ } + }); + m_layout->addRow(new QLabel(tr("Open with: \t")), pushbutton); + } +diff --git a/libpeony-qt/controls/property-page/computer-properties-page.h b/libpeony-qt/controls/property-page/computer-properties-page.h +index a87df91..78eb54f 100644 +--- a/libpeony-qt/controls/property-page/computer-properties-page.h ++++ b/libpeony-qt/controls/property-page/computer-properties-page.h +@@ -66,6 +66,17 @@ private: + QString m_uri; + QFormLayout *m_layout; + ++ quint64 m_home_counted_size = 0; ++ quint64 m_root_counted_size = 0; ++ quint64 m_usershare_counted_size = 0; ++ quint64 m_others_counted_size = 0; ++ ++ bool m_count_home_done = false; ++ bool m_count_usershare_done = false; ++ bool m_count_others_done = false; ++ ++ QTimer *m_timer = nullptr; ++ + // PropertiesWindowTabIface interface + public: + void saveAllChange(); +diff --git a/libpeony-qt/controls/property-page/open-with-properties-page.cpp b/libpeony-qt/controls/property-page/open-with-properties-page.cpp +index 81a97f0..18d667a 100644 +--- a/libpeony-qt/controls/property-page/open-with-properties-page.cpp ++++ b/libpeony-qt/controls/property-page/open-with-properties-page.cpp +@@ -256,8 +256,13 @@ NewFileLaunchDialog::NewFileLaunchDialog(const QString &uri, QWidget *parent) : + m_layout->addWidget(m_button_box); + + //add button translate +- m_button_box->button(QDialogButtonBox::Ok)->setText(tr("OK")); +- m_button_box->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); ++ QPushButton *okButton = m_button_box->button(QDialogButtonBox::Ok); ++ okButton->setText(tr("OK")); ++ okButton->setProperty("isImportant", true); ++ ++ QPushButton *cancelButton = m_button_box->button(QDialogButtonBox::Cancel); ++ cancelButton->setText(tr("Cancel")); ++ cancelButton->setProperty("useButtonPalette", true); + + connect(this, &QDialog::accepted, [=]() { + if (!m_launchHashList->m_actionList->currentItem()) +@@ -400,8 +405,13 @@ AllFileLaunchDialog::AllFileLaunchDialog(const QString &uri, QWidget *parent) : + m_layout->addWidget(m_button_box); + + //add button translate +- m_button_box->button(QDialogButtonBox::Ok)->setText(tr("OK")); +- m_button_box->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); ++ QPushButton *okButton = m_button_box->button(QDialogButtonBox::Ok); ++ okButton->setText(tr("OK")); ++ okButton->setProperty("isImportant", true); ++ ++ QPushButton *cancelButton = m_button_box->button(QDialogButtonBox::Cancel); ++ cancelButton->setText(tr("Cancel")); ++ cancelButton->setProperty("useButtonPalette", true); + + connect(this, &QDialog::accepted, [=]() { + if (!m_launchHashList->m_actionList->currentItem()) +diff --git a/libpeony-qt/controls/tool-bar/search-bar-container.cpp b/libpeony-qt/controls/tool-bar/search-bar-container.cpp +index 44dca2c..d623115 100644 +--- a/libpeony-qt/controls/tool-bar/search-bar-container.cpp ++++ b/libpeony-qt/controls/tool-bar/search-bar-container.cpp +@@ -48,8 +48,8 @@ ProgressLineEdit::ProgressLineEdit(QWidget *parent) + + connect(m_animation, &QVariantAnimation::valueChanged, this, [=](){ + if (m_animation->state() == QVariantAnimation::Running) { +- m_value = m_animation->currentValue().toReal(); +- if (m_searching && 0.7 < m_value/m_animation->endValue().toReal()*1.0) ++ m_value = m_animation->currentValue().toReal()/m_animation->endValue().toReal()*1.0; ++ if (m_searching && 0.7 < m_value) + m_animation->pause(); + update(); + } +@@ -86,7 +86,7 @@ void ProgressLineEdit::paintEvent(QPaintEvent *e) + p.setOpacity(0.25); + QBrush b; + QRect backgroundRect = this->rect(); +- backgroundRect.setWidth(m_value); ++ backgroundRect.setWidth(m_value*this->width()); + backgroundRect.adjust(2, 2,-2, -2); + p.fillRect(backgroundRect, this->palette().highlight().color()); + } +@@ -110,10 +110,11 @@ SearchBarContainer::SearchBarContainer(QWidget *parent): QWidget(parent) + + ProgressLineEdit *edit = new ProgressLineEdit(this); + m_search_box = edit; +- ++ setMaximumWidth(194); + QAction *searchAction = new QAction(m_search_box); + searchAction->setIcon(QIcon::fromTheme("edit-find-symbolic")); + m_search_box->addAction(searchAction,QLineEdit::LeadingPosition); ++ m_search_box->setPlaceholderText(tr("Search File")); + //fix bug#180920, contents and icon overlap issue + edit->setTextMargins(0, 0, 20, 0); + +@@ -152,15 +153,15 @@ SearchBarContainer::SearchBarContainer(QWidget *parent): QWidget(parent) + m_search_trigger.setInterval(500); + m_clear_action = true; + connect(&m_search_trigger, SIGNAL(timeout()), this, SLOT(startSearch())); +- connect(m_search_box, &QLineEdit::textChanged, [=](const QString &text) +- { ++// connect(m_search_box, &QLineEdit::textChanged, [=](const QString &text) ++// { + //fix input key words can not search issue, link to bug#77977 +- if (m_clear_action && ! m_search_trigger.isActive()) { +- m_search_trigger.start(); +- } else { +- m_clear_action = false; +- } +- }); ++// if (m_clear_action && ! m_search_trigger.isActive()) { ++// m_search_trigger.start(); ++// } else { ++// m_clear_action = false; ++// } ++// }); + + // connect(m_filter_box, &QComboBox::currentTextChanged, [=]() + // { +@@ -171,24 +172,11 @@ SearchBarContainer::SearchBarContainer(QWidget *parent): QWidget(parent) + QHBoxLayout* editlayout = new QHBoxLayout(edit); + editlayout->addStretch(); + +- QToolButton *searchButton = new QToolButton(edit); +- searchButton->setObjectName("toolButton"); +- searchButton->setStyle(ToolButtonStyle::getStyle()); +- searchButton->setIcon(QIcon::fromTheme("ukui-down-symbolic", QIcon(":/icons/ukui-down-symbolic"))); +- searchButton->setProperty("isWindowButton", 1); +- searchButton->setProperty("useIconHighlightEffect", 0x2); +- searchButton->setAutoRaise(true); +- editlayout->addWidget(searchButton,Qt::AlignRight); +- connect(searchButton, &QToolButton::clicked, this, [=]() { +- //qDebug() << "triggered search history!"; +- m_search_box->completer()->complete(); +- }); +- +- QToolButton* clearButton = new QToolButton(edit); +- clearButton->setObjectName("toolButton"); +- clearButton->setStyle(ToolButtonStyle::getStyle()); +- editlayout->addWidget(clearButton,Qt::AlignRight); +- clearButton->setAutoRaise(true); ++ m_stopSearchButton= new QToolButton(edit); ++ m_stopSearchButton->setObjectName("toolButton"); ++ m_stopSearchButton->setStyle(ToolButtonStyle::getStyle()); ++ editlayout->addWidget(m_stopSearchButton,Qt::AlignRight); ++ m_stopSearchButton->setAutoRaise(true); + // QToolButton* goToButton = new QToolButton(edit); + // goToButton->setAttribute(Qt::WA_TranslucentBackground); + // goToButton->setObjectName("toolButton"); +@@ -202,10 +190,10 @@ SearchBarContainer::SearchBarContainer(QWidget *parent): QWidget(parent) + editlayout->setMargin(2); + edit->setLayout(editlayout); + +- clearButton->setIcon(QIcon::fromTheme("edit-clear-symbolic")); +- clearButton->setProperty("isWindowButton", 1); +- clearButton->setProperty("useIconHighlightEffect", 2); +- clearButton->hide(); ++ m_stopSearchButton->setIcon(QIcon::fromTheme("edit-clear-symbolic")); ++ m_stopSearchButton->setProperty("isWindowButton", 1); ++ m_stopSearchButton->setProperty("useIconHighlightEffect", 2); ++ m_stopSearchButton->hide(); + + // goToButton->setIcon(QIcon::fromTheme("go-next-symbolic")); + // goToButton->setProperty("useIconHighlightEffect", true); +@@ -217,24 +205,10 @@ SearchBarContainer::SearchBarContainer(QWidget *parent): QWidget(parent) + // startSearch(); + // }); + +- connect(clearButton, &QToolButton::clicked, this, [=](){ +- //停止搜索 +- edit->clear(); +- }); +- connect(edit, &QLineEdit::textChanged, this, [=](const QString &text){ +- if(text.isEmpty()) +- { +- //goToButton->hide(); +- clearButton->hide(); +- searchButton->show(); +- } +- else +- { +- //goToButton->show(); +- clearButton->show(); +- searchButton->hide(); +- } +- }); ++ connect(m_stopSearchButton, &QToolButton::clicked, this, &SearchBarContainer::stopSearch); ++ ++ connect(edit, &QLineEdit::textChanged, this, &SearchBarContainer::changeSearchStatus); ++ + connect(this, &Peony::SearchBarContainer::updateSearchProgress, edit, &ProgressLineEdit::updateSearchProgress); + connect(m_list_view, SIGNAL(clicked(const QModelIndex &)), this, SLOT(onTableClicked(const QModelIndex &))); + } +@@ -260,6 +234,18 @@ void SearchBarContainer::onTableClicked(const QModelIndex &index) + m_model->setStringList(l); + } + ++void SearchBarContainer::changeSearchStatus() ++{ ++ if(!m_searching) { ++ m_searching = true; ++ m_stopSearchButton->show(); ++ if (!m_search_trigger.isActive()) { ++ m_search_trigger.start(); ++ } ++ ++ } ++} ++ + void SearchBarContainer::startSearch() + { + auto l = m_model->stringList(); +@@ -273,11 +259,22 @@ void SearchBarContainer::startSearch() + Q_EMIT this->returnPressed(); + } + ++void SearchBarContainer::stopSearch() ++{ ++ //停止搜索 ++ clearSearchBox(); ++ updateSearchProgress(false); ++ Q_EMIT updateLastLocationPath(); ++} ++ + void SearchBarContainer::clearSearchBox() + { + m_search_box->setText(""); + m_search_box->deselect(); ++ m_search_box->setPlaceholderText(tr("Search File")); + m_clear_action = true; ++ m_searching = false; ++ m_stopSearchButton->hide(); + //need stop search action + m_search_trigger.stop(); + } +diff --git a/libpeony-qt/controls/tool-bar/search-bar-container.h b/libpeony-qt/controls/tool-bar/search-bar-container.h +index c5298b5..5a3ccb2 100644 +--- a/libpeony-qt/controls/tool-bar/search-bar-container.h ++++ b/libpeony-qt/controls/tool-bar/search-bar-container.h +@@ -52,7 +52,7 @@ protected: + private: + QVariantAnimation *m_animation = nullptr; + bool m_searching = false; +- int m_value = 0; ++ double m_value = 0; + }; + + class SearchBarContainer : public QWidget +@@ -98,10 +98,13 @@ Q_SIGNALS: + void returnPressed(); + void filterUpdate(const int &index); + void updateSearchProgress(bool searching); ++ void updateLastLocationPath(); + + public Q_SLOTS: + void onTableClicked(const QModelIndex &index); ++ void changeSearchStatus(); + void startSearch(); ++ void stopSearch(); + void clearSearchBox(); + + private: +@@ -112,10 +115,12 @@ private: + + QStringListModel *m_model = nullptr; + QListView *m_list_view = nullptr; ++ QToolButton* m_stopSearchButton = nullptr; + + QTimer m_search_trigger; + + bool m_clear_action = true; ++ bool m_searching = false; + + QStringList m_file_type_list = {tr("all"), tr("file folder"), tr("image"), + tr("video"), tr("text file"), tr("audio"), tr("wps file"), tr("others") +diff --git a/libpeony-qt/convenient-utils/clipboard-utils.cpp b/libpeony-qt/convenient-utils/clipboard-utils.cpp +index 6cb00c2..7b7ba39 100644 +--- a/libpeony-qt/convenient-utils/clipboard-utils.cpp ++++ b/libpeony-qt/convenient-utils/clipboard-utils.cpp +@@ -85,7 +85,8 @@ ClipboardUtils::ClipboardUtils(QObject *parent) : QObject(parent) + gCutFileUris.clear(); + } + +- if (!QApplication::clipboard()->ownsClipboard()) { ++ qDebug()<<"XDG_SESSION_TYPE:"<<qgetenv("XDG_SESSION_TYPE")<<" ownsClipboard:"<<QApplication::clipboard()->ownsClipboard(); ++ if ("wayland" != qgetenv("XDG_SESSION_TYPE").toLower() && !QApplication::clipboard()->ownsClipboard()) { + gCutFileUris.clear(); + } + }); +@@ -218,22 +219,17 @@ void ClipboardUtils::setClipboardFiles(const QStringList &uris, bool isCut, bool + encodedUris << QString(encodeUrl); + } + data->setUrls(urls); +-// QString string = encodedUris.join(" "); +-// data->setData("peony-qt/encoded-uris", string.toUtf8()); +-// data->setText(string); ++ QString string = encodedUris.join(" "); ++ data->setData("peony-qt/encoded-uris", string.toUtf8()); ++ data->setText(string); + +- QString text; + QByteArray target = (isCut) ? "cut" : "copy"; + for (const QUrl &qurl : urls) { +- const QString &path = qurl.toLocalFile(); +- if (!path.isEmpty()) { +- text += path + '\n'; +- } + target.append("\n"); + target.append(qurl.toString()); + } +- data->setText(text.endsWith('\n') ? text.left(text.length() - 1) : text); + data->setData("x-special/gnome-copied-files", target); ++ + QVariant isSearchData = QVariant(isSearch); + data->setData("peony-qt/is-search", isSearchData.toByteArray()); + +diff --git a/libpeony-qt/convenient-utils/convenient-utils.cpp b/libpeony-qt/convenient-utils/convenient-utils.cpp +new file mode 100644 +index 0000000..f2f61d1 +--- /dev/null ++++ b/libpeony-qt/convenient-utils/convenient-utils.cpp +@@ -0,0 +1,82 @@ ++#include "convenient-utils.h" ++#include "file-enumerator.h" ++#include "file-item-model.h" ++#include "file-item-proxy-filter-sort-model.h" ++#include "file-info-manager.h" ++#include "file-info-job.h" ++#include "file-infos-job.h" ++#include "file-info.h" ++#include "file-utils.h" ++ ++#include <QObject> ++#include <QUrl> ++ ++using namespace Peony; ++ ++ConvenientUtils *ConvenientUtils::global_instance = nullptr; ++ ++ConvenientUtils *ConvenientUtils::getInstance() ++{ ++ if (!global_instance) { ++ global_instance = new ConvenientUtils; ++ } ++ return global_instance; ++} ++ ++ConvenientUtils::ConvenientUtils(QObject *parent) : QObject(parent) ++{ ++ ++} ++ ++QStringList ConvenientUtils::getFileUrisInSequence(const QString &uri) const ++{ ++ QStringList orderedFileUris; ++ /* uri遍历方式获取子项 */ ++ FileEnumerator e; ++ e.setEnumerateDirectory(uri); ++ e.enumerateSync(); ++ std::vector<std::shared_ptr<FileInfo> > fileInfoVec; ++ for (auto fileInfo : e.getChildren()) { ++ FileInfoJob infoJob(fileInfo); ++ infoJob.querySync(); ++ fileInfoVec.push_back(fileInfo); ++ } ++ ++ FileItemModel *model = new FileItemModel(); ++ FileItemProxyFilterSortModel *proxy_model = new FileItemProxyFilterSortModel(); ++ proxy_model->setSourceModel(model); ++ ++ auto fileInfo = FileInfo::fromUri(uri); ++ FileInfoJob infoJob(fileInfo); ++ infoJob.querySync(); ++ auto item = new FileItem(fileInfo, nullptr, model, nullptr); ++ ++ model->insetFileInfoData(fileInfoVec, item); ++ proxy_model->checkSettingsAndSort(); ++ orderedFileUris = proxy_model->getAllFileUris(); ++ ++ if(model){ ++ delete model; ++ model = nullptr; ++ } ++ if(proxy_model){ ++ delete proxy_model; ++ proxy_model = nullptr; ++ } ++ return orderedFileUris; ++} ++ ++QStringList ConvenientUtils::getFilePathsInSequence(const QString &filePath) const ++{ ++ QString encodeUri = FileUtils::urlEncode("file://" + filePath); ++ QStringList orderedFileUris = getFileUrisInSequence(encodeUri); ++ ++ QStringList orderedFilePaths; ++ for (QString fileUri : orderedFileUris) { ++ QString filePath = QUrl(fileUri).path(); ++ orderedFilePaths.append(filePath); ++ } ++ return orderedFilePaths; ++} ++ ++ +diff --git a/libpeony-qt/convenient-utils/convenient-utils.h b/libpeony-qt/convenient-utils/convenient-utils.h +new file mode 100644 +index 0000000..2d3b85d +--- /dev/null ++++ b/libpeony-qt/convenient-utils/convenient-utils.h +@@ -0,0 +1,28 @@ ++#ifndef CONVENIENTUTILS_H ++#define CONVENIENTUTILS_H ++ ++#include <QStringList> ++#include <QObject> ++ ++#include "peony-core_global.h" ++ ++namespace Peony { ++ ++class PEONYCORESHARED_EXPORT ConvenientUtils: public QObject ++{ ++ Q_OBJECT ++public: ++ static ConvenientUtils *getInstance(); ++ ++ QStringList getFileUrisInSequence(const QString& uri) const; /* 传入和输出都是encoded uri */ ++ QStringList getFilePathsInSequence(const QString& filePath) const;/* 传入和输出都是decoded absolute path,目前只用于‘file://’开头的目录 */ ++ ++private: ++ explicit ConvenientUtils(QObject *parent = nullptr); ++ ~ConvenientUtils(){} ++ ++ static ConvenientUtils *global_instance; ++}; ++} ++ ++#endif // CONVENIENTUTILS_H +diff --git a/libpeony-qt/convenient-utils/convenient-utils.pri b/libpeony-qt/convenient-utils/convenient-utils.pri +index 8caebde..0336201 100644 +--- a/libpeony-qt/convenient-utils/convenient-utils.pri ++++ b/libpeony-qt/convenient-utils/convenient-utils.pri +@@ -6,6 +6,7 @@ include(disc/disc.pri) + HEADERS += \ + $$PWD/audio-play-manager.h \ + $$PWD/clipboard-utils.h \ ++ $$PWD/convenient-utils.h \ + $$PWD/datacdrom.h \ + $$PWD/file-operation-utils.h \ + $$PWD/systemd-bus-accounts.h \ +@@ -14,6 +15,7 @@ HEADERS += \ + SOURCES += \ + $$PWD/audio-play-manager.cpp \ + $$PWD/clipboard-utils.cpp \ ++ $$PWD/convenient-utils.cpp \ + $$PWD/datacdrom.cpp \ + $$PWD/file-operation-utils.cpp \ + $$PWD/systemd-bus-accounts.cpp \ +diff --git a/libpeony-qt/convenient-utils/datacdrom.cpp b/libpeony-qt/convenient-utils/datacdrom.cpp +index b22960f..c31f66f 100644 +--- a/libpeony-qt/convenient-utils/datacdrom.cpp ++++ b/libpeony-qt/convenient-utils/datacdrom.cpp +@@ -33,6 +33,7 @@ + #include <QCoreApplication> + #include <QFileInfo> + #include <QDebug> ++#include <QStringListIterator> + + using namespace Peony; + +@@ -536,43 +537,71 @@ void DataCDROM::DVDRWCapacity() + + process.start("/usr/bin/dvd+rw-mediainfo", deviceName); + process.waitForFinished(10000); +- QString result = process.readAllStandardOutput(); +- QStringList ss = result.split("\n"); +- QStringList sss; +- +- for (i = ss.size() - 1; i > 0; --i) +- { +- if (ss.at(i).startsWith("READ FORMAT CAPACITIES:")) break; ++ QString dvdrwMediaInfoResult = process.readAllStandardOutput(); ++ QStringList devMediaInfoList = dvdrwMediaInfoResult.split("\n"); ++ QStringList dvdRomCapaitiesInfo; ++ ++ QStringListIterator iter_devMediaInfoList(devMediaInfoList); ++ while (iter_devMediaInfoList.hasNext()) { ++ QString item = iter_devMediaInfoList.next(); ++ if (item.startsWith("READ FORMAT CAPACITIES:")) ++ break; + } + +- if (m_oMediumType.contains("DVD+RW") && ss.size() >= i+1) +- { +- ss = ss.takeAt(i + 1).split("="); +- //ss = ss.last().split("="); +- m_u64Capacity = ss.last().toULong(); ++ bool states_err = false; ++ ++ if (iter_devMediaInfoList.hasNext()) { ++ QString item = iter_devMediaInfoList.next(); ++ if(item.contains("error")) { ++ states_err = true; ++ m_u64Capacity = 0; ++ return; ++ } ++ dvdRomCapaitiesInfo = item.split("="); ++ m_u64Capacity = dvdRomCapaitiesInfo.last().toULong(); ++ qDebug()<< "DVD+RW get capacity is : " << m_u64Capacity; + } +- if (m_oMediumType.contains("DVD-RW") && ss.size() >= i+2) +- { +- //解决bug:70940和83628擦除后总容量显示错误 +-#if 0 +- sss = ss.takeAt(i + 1).split("="); +- t = 0; +- u = 0; +- u = sss.last().toULong();//unformatted的容量 +- sss = ss.takeAt(i + 2).split("="); +- t = sss.last().toULong();//00h(800h)的容量 +- if (t > u) m_u64Size = t - u; +- else m_u64Size = u; +- //出现下面这种情况导致bug出现 +- //no media: 4101552*2048=8399978496 +- //00h(800): 2297888*2048=4706074624 +-#else +- sss = ss.takeAt(i + 2).split("="); +- auto t = sss.last().toULong(); +- m_u64Capacity = t; +-#endif ++ ++ if (!states_err && m_oMediumType.contains("DVD-RW") && iter_devMediaInfoList.hasNext()) { ++ QString item = iter_devMediaInfoList.next(); ++ dvdRomCapaitiesInfo.clear(); ++ dvdRomCapaitiesInfo = item.split("="); ++ m_u64Capacity = dvdRomCapaitiesInfo.last().toULong(); ++ qDebug()<< "DVD-RW get capacity is : " << m_u64Capacity; + } + ++// for (i = devMediaInfoList.size() - 1; i > 0; --i) ++// { ++// if (devMediaInfoList.at(i).startsWith("READ FORMAT CAPACITIES:")) break; ++// } ++ ++// if (m_oMediumType.contains("DVD+RW") && devMediaInfoList.size() >= i+1) ++// { ++// dvdRomCapaitiesInfo = devMediaInfoList.takeAt(i + 1).split("="); ++// m_u64Capacity = dvdRomCapaitiesInfo.last().toULong(); ++// } ++// if (m_oMediumType.contains("DVD-RW") && devMediaInfoList.size() >= i+2) ++// { ++// //解决bug:70940和83628擦除后总容量显示错误 ++//#if 0 ++// sss = ss.takeAt(i + 1).split("="); ++// t = 0; ++// u = 0; ++// u = sss.last().toULong();//unformatted的容量 ++// sss = ss.takeAt(i + 2).split("="); ++// t = sss.last().toULong();//00h(800h)的容量 ++// if (t > u) m_u64Size = t - u; ++// else m_u64Size = u; ++// //出现下面这种情况导致bug出现 ++// //no media: 4101552*2048=8399978496 ++// //00h(800): 2297888*2048=4706074624 ++//#else ++// dvdRomCapaitiesInfo = devMediaInfoList.takeAt(i + 2).split("="); ++// auto t = dvdRomCapaitiesInfo.last().toULong(); ++// m_u64Capacity = t; ++//#endif ++// } ++ + // for (index = dvdInfo.size() - 1; index > 0; --index){ + // if (dvdInfo.at(index).startsWith("READ FORMAT CAPACITIES:")) { + // QStringList formatCapacity = dvdInfo.takeAt(index + 1).split("="); +diff --git a/libpeony-qt/convenient-utils/disc/disccontrol.cpp b/libpeony-qt/convenient-utils/disc/disccontrol.cpp +index bcdabca..5c744e1 100644 +--- a/libpeony-qt/convenient-utils/disc/disccontrol.cpp ++++ b/libpeony-qt/convenient-utils/disc/disccontrol.cpp +@@ -924,7 +924,7 @@ QString DiscControl::prepareFileBeforeBurn(const QString& srcFile){ + QProcess *createHardlink = new QProcess(); + hardLinkFileName = srcFileAttr.fileName(); //同名硬链接 + hardLinkParentDir = QDir::homePath()+"/.cache/KylinBurner/"; +- //hardLinkParentDir = srcFileAttr.canonicalPath(() + ".cache/KylinBurner";//jxyh项目修改硬链接路径 ++// hardLinkParentDir = srcFileAttr.canonicalPath(() + ".cache/KylinBurner";//jxyh项目修改硬链接路径 + //先确保指定目录存在,不存在则递归创建 + dirObject.setPath(hardLinkParentDir); + if(!dirObject.exists()){ +@@ -1034,23 +1034,19 @@ bool DiscControl::supportUdf() const{ + //if(mProfile & (MEDIA_CD_RW|MEDIA_DVD_RW_ALL|MEDIA_DVD_PLUS_RW)) + + // 判断当前系统中是否集成有udfclient包,如果没有则不支持DVD+RW格式化为udf格式 +- bool isExistUDFClient = false; + QFileInfo binfile; + binfile.setFile("/bin/newfs_udf"); +- if(!isExistUDFClient && binfile.exists() && binfile.isExecutable()) { +- isExistUDFClient = true; ++ if(!binfile.exists() || !binfile.isExecutable()) { ++ return false; + } ++ + binfile.setFile("/usr/bin/newfs_udf"); +- if(!isExistUDFClient && binfile.exists() && binfile.isExecutable()) { +- isExistUDFClient = true; +- } +- if (!isExistUDFClient) { ++ if(!binfile.exists() || !binfile.isExecutable()) { + return false; + } + +- if(mProfile & MEDIA_DVD_PLUS_RW) { //2209仅提供DVD+RW的udf格式化 ++ if(mProfile & MEDIA_DVD_PLUS_RW) //2209仅提供DVD+RW的udf格式化 + return true; +- } + + return false; + } +diff --git a/libpeony-qt/convenient-utils/file-operation-utils.cpp b/libpeony-qt/convenient-utils/file-operation-utils.cpp +index 011e5f7..35f4c12 100644 +--- a/libpeony-qt/convenient-utils/file-operation-utils.cpp ++++ b/libpeony-qt/convenient-utils/file-operation-utils.cpp +@@ -33,6 +33,7 @@ + + #include "file-untrash-operation.h" + #include "file-count-operation.h" ++#include "file-properties-operation.h" + + #include "file-info-job.h" + #include "file-info.h" +@@ -58,6 +59,7 @@ static FileOperation *trashInternal(const QStringList &uris, bool addHistory, bo + FileOperation *op = nullptr; + bool canNotTrash = false; + bool isBigFile = false; ++ bool skipDialog = false; + + QString userPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); + +@@ -88,6 +90,21 @@ static FileOperation *trashInternal(const QStringList &uris, bool addHistory, bo + } + } + ++ // 只读文件系统场景需要跳过确认弹框,直接走文件操作异常报错弹框流程 ++ // fixme: 这里的判断条件不充分,只是必要条件 ++ if (uris.count() > 0) { ++ auto uri = uris.first(); ++ auto parentUri = FileUtils::getParentUri(uri); ++ auto info = FileInfo::fromUri(parentUri); ++ if (info->isEmptyInfo()) { ++ FileInfoJob j(info); ++ j.querySync(); ++ } ++ if (!info->canWrite()) { ++ skipDialog = true; ++ } ++ } ++ + if (!canNotTrash) { + quint64 total_size = 0; + const quint64 ONE_GIB_SIZE = 1024*1024*1024; +@@ -140,7 +157,7 @@ static FileOperation *trashInternal(const QStringList &uris, bool addHistory, bo + // } + // } + +- if (canNotTrash) { ++ if (canNotTrash && !skipDialog) { + Peony::AudioPlayManager::getInstance()->playWarningAudio(); + //task #155670,155671 improve delete file permanently message + QString message; +@@ -162,8 +179,16 @@ static FileOperation *trashInternal(const QStringList &uris, bool addHistory, bo + "these file will not be recoverable.").arg(uris.length()); + } + +- auto result = QMessageBox::question(nullptr, "", message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); +- if (result == QMessageBox::Yes) { ++ //auto result = QMessageBox::question(nullptr, "", message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); ++ QMessageBox msgBox; ++ msgBox.setText(message); ++ msgBox.setIcon(QMessageBox::Question); ++ QPushButton *deleteButton = msgBox.addButton(QObject::tr("Delete"), QMessageBox::AcceptRole); ++ msgBox.addButton(QObject::tr("Cancel"), QMessageBox::RejectRole); ++ deleteButton->setDefault(true); ++ ++ int result = msgBox.exec(); ++ if (msgBox.clickedButton() == deleteButton) { + op = FileOperationUtils::remove(uris); + } + return op; +@@ -378,6 +403,42 @@ FileOperation *FileOperationUtils::create(const QString &destDirUri, const QStri + return createOp; + } + ++FileOperation *FileOperationUtils::setReadOnly(const QStringList &srcUris, bool readOnly, bool recursive) ++{ ++ auto fileOpMgr = FileOperationManager::getInstance(); ++ FilePropertiesOperation::Options options = FilePropertiesOperation::ChangeReadOnly; ++ if (recursive) { ++ options |= FilePropertiesOperation::ChangeRecursively; ++ } ++ auto readOnlyOp = new FilePropertiesOperation(srcUris, options, false, readOnly); ++ fileOpMgr->startOperation(readOnlyOp, false); ++ return readOnlyOp; ++} ++ ++FileOperation *FileOperationUtils::setHidden(const QStringList &srcUris, bool hidden, bool recursive) ++{ ++ auto fileOpMgr = FileOperationManager::getInstance(); ++ FilePropertiesOperation::Options options = FilePropertiesOperation::ChangeHidden; ++ if (recursive) { ++ options |= FilePropertiesOperation::ChangeRecursively; ++ } ++ auto hiddenOp = new FilePropertiesOperation(srcUris, options, hidden, false); ++ fileOpMgr->startOperation(hiddenOp, false); ++ return hiddenOp; ++} ++ ++FileOperation *FileOperationUtils::setReadOnlyAndHidden(const QStringList &srcUris, bool readOnly, bool hidden, bool recursive) ++{ ++ auto fileOpMgr = FileOperationManager::getInstance(); ++ FilePropertiesOperation::Options options = FilePropertiesOperation::Options(FilePropertiesOperation::ChangeReadOnly|FilePropertiesOperation::ChangeHidden); ++ if (recursive) { ++ options |= FilePropertiesOperation::ChangeRecursively; ++ } ++ auto propertiesOp = new FilePropertiesOperation(srcUris, options, hidden, readOnly); ++ fileOpMgr->startOperation(propertiesOp); ++ return propertiesOp; ++} ++ + void FileOperationUtils::executeRemoveActionWithDialog(const QStringList &uris) + { + if (uris.isEmpty()) +@@ -394,9 +455,16 @@ void FileOperationUtils::executeRemoveActionWithDialog(const QStringList &uris) + "these file will not be recoverable.").arg(uris.length()); + } + +- result = QMessageBox::question(nullptr, "", message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); ++ //result = QMessageBox::question(nullptr, "", message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); ++ QMessageBox msgBox; ++ msgBox.setText(message); ++ msgBox.setIcon(QMessageBox::Question); ++ QPushButton *deleteButton = msgBox.addButton(QObject::tr("Delete"), QMessageBox::AcceptRole); ++ msgBox.addButton(QObject::tr("Cancel"), QMessageBox::RejectRole); ++ deleteButton->setDefault(true); + +- if (result == QMessageBox::Yes) { ++ result = msgBox.exec(); ++ if (msgBox.clickedButton() == deleteButton) { + FileOperationUtils::remove(uris); + } + } +diff --git a/libpeony-qt/convenient-utils/file-operation-utils.h b/libpeony-qt/convenient-utils/file-operation-utils.h +index 73a9d9a..9a85ef2 100644 +--- a/libpeony-qt/convenient-utils/file-operation-utils.h ++++ b/libpeony-qt/convenient-utils/file-operation-utils.h +@@ -51,6 +51,10 @@ public: + static FileOperation *restore(const QStringList &urisInTrash); + static FileOperation *create(const QString &destDirUri, const QString &name = nullptr, CreateTemplateOperation::Type type = CreateTemplateOperation::EmptyFile); + ++ static FileOperation *setReadOnly(const QStringList &srcUris, bool readOnly, bool recursive = false); ++ static FileOperation *setHidden(const QStringList &srcUris, bool hidden, bool recursive = false); ++ static FileOperation *setReadOnlyAndHidden(const QStringList &srcUris, bool readOnly, bool hidden, bool recursive = false); ++ + static void executeRemoveActionWithDialog(const QStringList &uris); + + static bool leftNameIsDuplicatedFileOfRightName(const QString &left, const QString &right); +diff --git a/libpeony-qt/extensions-manager-widget.cpp b/libpeony-qt/extensions-manager-widget.cpp +index 8c53ed6..fc221a2 100644 +--- a/libpeony-qt/extensions-manager-widget.cpp ++++ b/libpeony-qt/extensions-manager-widget.cpp +@@ -30,6 +30,7 @@ + #include <QPluginLoader> + #include <QDebug> + #include <QHBoxLayout> ++#include <QCloseEvent> + + static Peony::ExtensionsManagerWidget *global_instance = nullptr; + +@@ -41,6 +42,14 @@ Peony::ExtensionsManagerWidget *Peony::ExtensionsManagerWidget::getInstance() + return global_instance; + } + ++void Peony::ExtensionsManagerWidget::deleteInstance() ++{ ++ if (global_instance) { ++ global_instance->deleteLater(); ++ global_instance = nullptr; ++ } ++} ++ + Peony::ExtensionsManagerWidget::ExtensionsManagerWidget(QWidget *parent) + :QWidget(parent) + { +@@ -49,9 +58,6 @@ Peony::ExtensionsManagerWidget::ExtensionsManagerWidget(QWidget *parent) + + Peony::ExtensionsManagerWidget::~ExtensionsManagerWidget() + { +- for (auto plugin : m_pluginMap) { +- delete plugin; +- } + m_pluginMap.clear(); + } + +@@ -62,7 +68,7 @@ void Peony::ExtensionsManagerWidget::initUI() + this->resize(EXTENSIONS_SHOW_WIDTH, EXTENSIONS_SHOW_HEIGHT); + this->setMinimumWidth(EXTENSIONS_SHOW_WIDTH); + this->setWindowTitle(tr("Extensions Manager")); +- this->setWindowIcon(QIcon::fromTheme("system-file-manager")); ++ //this->setWindowIcon(QIcon::fromTheme("system-file-manager")); + + m_mainLayout = new QVBoxLayout(this); + m_useLabel = new QLabel(tr("Available extensions")); +@@ -106,11 +112,9 @@ void Peony::ExtensionsManagerWidget::initUI() + + connect(m_cancelBtn, &QPushButton::clicked, this, [=](){ + this->close(); +- if (global_instance) { +- delete global_instance; +- global_instance = nullptr; +- } ++ deleteInstance(); + }); ++ + } + + void Peony::ExtensionsManagerWidget::initTableWidget() +@@ -145,11 +149,11 @@ void Peony::ExtensionsManagerWidget::initTableWidget() + int count = m_pluginMap.count(); + m_tableWidget->setRowCount(count); + +- QMap<QString, PluginInterface*>::iterator iter; ++ QMap<QString, PluginInfo>::iterator iter; + int row = 0; + for (iter = m_pluginMap.begin(); iter != m_pluginMap.end(); ++iter) { + QString filePath = iter.key(); +- PluginInterface* iface = iter.value(); ++ PluginInfo info = iter.value(); + + //Add checkBox + m_tableWidget->setCellWidget(row, 0, nullptr); +@@ -165,16 +169,15 @@ void Peony::ExtensionsManagerWidget::initTableWidget() + l->addWidget(checkBox); + m_tableWidget->setCellWidget(row, 0, w); + +- QIcon icon = iface->icon(); + QLabel *iconLabel = new QLabel(); + iconLabel->setProperty("useIconHighlightEffect", 0x2); +- iconLabel->setPixmap(QIcon::fromTheme(icon.name(), QIcon::fromTheme("unknown")).pixmap(QSize(24, 24))); ++ iconLabel->setPixmap(QIcon::fromTheme(info.icon.name(), QIcon::fromTheme("unknown")).pixmap(QSize(24, 24))); + iconLabel->setAlignment(Qt::AlignCenter); + m_tableWidget->setCellWidget(row, 1, iconLabel); + +- QTableWidgetItem* itemC2 = new QTableWidgetItem(iface->description()); ++ QTableWidgetItem* itemC2 = new QTableWidgetItem(info.description); + itemC2->setFlags(itemC2->flags() | Qt::ItemIsSelectable); +- itemC2->setToolTip(iface->description()); ++ itemC2->setToolTip(info.description); + m_tableWidget->setItem(row, 2, itemC2); + row++; + } +@@ -196,6 +199,9 @@ void Peony::ExtensionsManagerWidget::initExtensionInfo() + + Q_FOREACH(QString fileName, pluginsDir.entryList(QDir::Files)) { + QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName)); ++ if (fileName == "libsafe-context-menu.so") { ++ pluginLoader.setLoadHints(pluginLoader.loadHints() | QLibrary::DeepBindHint); ++ } + qDebug()<<pluginLoader.fileName(); + qDebug()<<pluginLoader.metaData(); + qDebug()<<pluginLoader.load(); +@@ -209,7 +215,10 @@ void Peony::ExtensionsManagerWidget::initExtensionInfo() + PluginInterface *piface = dynamic_cast<PluginInterface*>(plugin); + if (!piface) + continue; +- m_pluginMap.insert(pluginLoader.fileName(), piface); ++ PluginInfo info; ++ info.icon = piface->icon(); ++ info.description = piface->description(); ++ m_pluginMap.insert(pluginLoader.fileName(), info); + + QFileInfo fileInfo(pluginLoader.fileName()); + if (fileInfo.exists()) { +@@ -240,3 +249,10 @@ void Peony::ExtensionsManagerWidget::addSeparator() + separate->setEnabled(false); + m_mainLayout->addWidget(separate); + } ++ ++void Peony::ExtensionsManagerWidget::closeEvent(QCloseEvent *event) ++{ ++ if (event) { ++ deleteInstance(); ++ } ++} +diff --git a/libpeony-qt/extensions-manager-widget.h b/libpeony-qt/extensions-manager-widget.h +index 6572b59..8f34f64 100644 +--- a/libpeony-qt/extensions-manager-widget.h ++++ b/libpeony-qt/extensions-manager-widget.h +@@ -39,17 +39,27 @@ + + namespace Peony { + ++struct PluginInfo { ++ QIcon icon; ++ QString description; ++}; ++ ++ + class PEONYCORESHARED_EXPORT ExtensionsManagerWidget : public QWidget + { + Q_OBJECT + public: + static ExtensionsManagerWidget *getInstance(); ++ static void deleteInstance(); + void initUI(); + void initTableWidget(); + void initExtensionInfo(); + bool updateCheckBox(const QString &path); + void addSeparator(); + ++protected: ++ void closeEvent(QCloseEvent *event) override; ++ + private: + explicit ExtensionsManagerWidget(QWidget *parent = nullptr); + ~ExtensionsManagerWidget(); +@@ -61,7 +71,7 @@ private: + QPushButton *m_okBtn = nullptr; + QPushButton *m_cancelBtn = nullptr; + +- QMap<QString, PluginInterface*> m_pluginMap; ++ QMap<QString, PluginInfo> m_pluginMap; + QStringList m_disabledList; + }; + } +diff --git a/libpeony-qt/file-copy.cpp b/libpeony-qt/file-copy.cpp +index e9d62f8..9063b14 100644 +--- a/libpeony-qt/file-copy.cpp ++++ b/libpeony-qt/file-copy.cpp +@@ -109,13 +109,14 @@ void FileCopy::sync(const GFile* destFile) + // it's not possible + g_return_if_fail(uri && path); + ++ QString uriStr = QString::fromUtf8(uri); + // uri is start with "file://" + QString gvfsPath = QString("/run/user/%1/gvfs/").arg(getuid()); +- if (0 != g_ascii_strncasecmp(uri, "file:///", 8) || g_strstr_len(uri, strlen(uri), gvfsPath.toUtf8().constData())) { ++ if (!uriStr.startsWith("file:///") || uriStr.contains(gvfsPath)) { + return; + } + +- if(mTotalSize < BUF_SIZE * SYNC_INTERVAL || mIsDestFileLocal) { ++ if(mIsDestFileLocal) { + return; + } + +@@ -432,7 +433,7 @@ out: + if (FINISHED == mStatus && g_file_query_exists(destFile, nullptr)) { + // copy file attribute + // It is possible that some file systems do not support file attributes +- //从只读文件系统复制文件,默认给与文件可写权限,hgzs项目前场反馈需求,task#138082 ++ //从只读文件系统复制文件,默认给与文件可写权限,海关总署项目前场反馈需求,task#138082 + g_file_copy_attributes(srcFile, destFile, mParentFlags, nullptr, &error); + if (nullptr != error) { + qWarning() << "copy attribute error:" << error->code << " --- " << error->message; +diff --git a/libpeony-qt/file-info-job.cpp b/libpeony-qt/file-info-job.cpp +index 79dbedc..3f23eb9 100644 +--- a/libpeony-qt/file-info-job.cpp ++++ b/libpeony-qt/file-info-job.cpp +@@ -29,6 +29,7 @@ + #include "file-label-model.h" + + #include "emblem-provider.h" ++#include "file-utils.h" + + #include <gio/gdesktopappinfo.h> + #include <global-settings.h> +@@ -247,9 +248,14 @@ void FileInfoJob::queryFileDisplayName(GFileInfo* new_info){ + + info->m_display_name = QString (g_file_info_get_display_name(new_info)); + info->m_finalDisplayName = info->m_display_name; +- if (info->isDesktopFile()) { ++ if (info->uri().endsWith(".desktop")) { + info->m_desktop_name = info->displayName(); + QUrl url = info->uri(); ++ if (url.scheme() == "trash" && info->targetUri() != "") ++ { ++ url = QUrl(info->targetUri()); ++ info->setProperty("enable_trash_target", true); ++ } + GKeyFile *desktop_key_file = g_key_file_new(); + bool is_loaded = g_key_file_load_from_file(desktop_key_file, url.path().toUtf8(), G_KEY_FILE_NONE, nullptr); + if (!is_loaded) { +@@ -283,11 +289,17 @@ void FileInfoJob::queryFileDisplayName(GFileInfo* new_info){ + name_char = g_key_file_get_string(desktop_key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, nullptr); + } + ++ if(info->canExecute() || (QFileInfo::exists(url.path().toUtf8()) && url.path().startsWith("/usr/share/applications/"))) ++ { ++ info->m_display_name = name_char; ++ } ++ else if (FileUtils::isExecuteTargetUribyTrashUri(url, info)) ++ { ++ info->m_display_name = name_char; ++ } ++ + if (name_char) { +- QString name = name_char; + g_free(name_char); +- +- info->m_display_name = name; + } + info->m_finalDisplayName = info->m_display_name; + g_key_file_free(desktop_key_file); +@@ -320,8 +332,8 @@ void FileInfoJob::refreshInfoContents(GFileInfo *new_info) + queryFileType(new_info); + + /* 获取info的G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN字段的值 */ +- info->setProperty(G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN, g_file_info_get_attribute_boolean(new_info, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN)); +- ++ info->m_is_hidden = g_file_info_get_attribute_boolean(new_info, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN); ++ info->setProperty(G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN, info->m_is_hidden); + info->m_is_symbol_link = g_file_info_get_attribute_boolean(new_info, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK); + if (g_file_info_has_attribute(new_info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) { + info->m_can_read = g_file_info_get_attribute_boolean(new_info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ); +diff --git a/libpeony-qt/file-info.cpp b/libpeony-qt/file-info.cpp +index 3081f24..b1ff900 100644 +--- a/libpeony-qt/file-info.cpp ++++ b/libpeony-qt/file-info.cpp +@@ -20,14 +20,17 @@ + * + */ + #include <gio/gunixmounts.h> +-#include "file-info.h" ++#include <gio/gio.h> ++#include <gio/gdesktopappinfo.h> + ++#include "file-info.h" + #include "file-info-manager.h" + #include "file-info-job.h" + #include "file-meta-info.h" + #include "file-utils.h" + #include "thumbnail-manager.h" + #include "emblem-provider.h" ++#include "global-settings.h" + + #include <QUrl> + #include <QtDBus/QDBusConnection> +@@ -296,18 +299,23 @@ const QString FileInfo::getFinalDisplayName() + if (isEmptyInfo()) + return nullptr; + +- bool isMountPoint; +- QString unixDevice,deviceName; +- +- unixDevice = unixDeviceFile(); +- isMountPoint = FileUtils::isMountPoint(m_uri); +- + if(m_uri == "file:///DATA" + || m_uri == "file:///data" + || m_target_uri == "file:///data") + { + return tr("data"); + } ++ if(GlobalSettings::getInstance()->isDesktopStartUp()){ ++ return m_display_name; ++ } ++ ++ bool isMountPoint; ++ QString unixDevice,deviceName; ++ ++ unixDevice = unixDeviceFile(); ++ isMountPoint = FileUtils::isMountPoint(m_uri); ++ ++ + + if((nullptr != m_display_name) + && (!isMountPoint +@@ -379,6 +387,7 @@ FileInfo &FileInfo::operator=(const FileInfo &other) + this->m_finalDisplayName = other.m_finalDisplayName; + this->m_create_time = other.m_create_time; + this->m_create_date = other.m_create_date; ++ this->m_is_hidden = other.m_is_hidden; + this->setProperty("orig-path", other.property("orig-path")); + this->setProperty(G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN, other.property(G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN)); + } +@@ -394,6 +403,83 @@ QString FileInfo::updateIconName(const QString &uri, const QString &iconName) co + return iconName; + } + ++ ++bool FileInfo::isExistTargetOfSymlink() const ++{ ++ /** ++ * if the current file is a symbolic link and m_symlink_target is not null ++ * and the path to the file where m_symlink_target is located exists, returns false, otherwise returns true. ++ */ ++ if (m_is_symbol_link && !m_symlink_target.isEmpty() && !QFileInfo(m_symlink_target).exists()) { ++ return false; ++ } ++ return true; ++} ++ ++QIcon FileInfo::getIcon() ++{ ++ // Try getting thumbnail first ++ auto icon = ThumbnailManager::getInstance()->tryGetThumbnail(uri()); ++ if (!icon.isNull()) { ++ return icon; ++ } ++ ++ // Handle desktop file ++ if (m_uri.endsWith(".desktop")) { ++ QUrl url = uri(); ++ if (url.scheme() == "trash" && !targetUri().isEmpty()) { ++ url = QUrl(targetUri()); ++ } ++ ++ GDesktopAppInfo *desktop_info = g_desktop_app_info_new_from_filename(url.path().toUtf8()); ++ if (desktop_info) { ++ QString desktopIcon = g_desktop_app_info_get_string(desktop_info, "Icon"); ++ if (!desktopIcon.isEmpty()) { ++ if (desktopIcon.startsWith("/")) { ++ // absolute path, direct load ++ QIcon directIcon(desktopIcon); ++ if (!directIcon.isNull()) { ++ ThumbnailManager::getInstance()->insertOrUpdateThumbnail(uri(), directIcon); ++ g_object_unref(desktop_info); ++ return directIcon; ++ } ++ } else { ++ // icon name, loaded from theme ++ QIcon themeIcon = QIcon::fromTheme(desktopIcon, QIcon::fromTheme("unknown")); ++ if (themeIcon.name() != "unknown") { ++ ThumbnailManager::getInstance()->insertOrUpdateThumbnail(uri(), themeIcon); ++ g_object_unref(desktop_info); ++ return themeIcon; ++ } ++ } ++ } ++ g_object_unref(desktop_info); ++ } ++ } ++ ++ // Get icon name and try theme icon ++ QString iconName = this->iconName(); ++ ++ // Try icon name without suffix as last resort ++ QFileInfo iconInfo(iconName); ++ if (iconInfo.exists()) { ++ QString iconNameWithoutSuffix = iconInfo.completeBaseName(); ++ icon = QIcon::fromTheme(iconNameWithoutSuffix, QIcon::fromTheme("unknown")); ++ if (icon.name() != "unknown") { ++ ThumbnailManager::getInstance()->insertOrUpdateThumbnail(uri(), icon); ++ return icon; ++ } ++ } ++ ++ icon = QIcon::fromTheme(iconName, QIcon::fromTheme("unknown")); ++ if (icon.name() != "unknown" ) { ++ ThumbnailManager::getInstance()->insertOrUpdateThumbnail(uri(), icon); ++ return icon; ++ } ++ ++ return QIcon::fromTheme("unknown"); ++} ++ + const QString FileInfo::unixDeviceFile() + { + GFile* file; +diff --git a/libpeony-qt/file-info.h b/libpeony-qt/file-info.h +index fedf0f9..c6dfe46 100644 +--- a/libpeony-qt/file-info.h ++++ b/libpeony-qt/file-info.h +@@ -35,6 +35,7 @@ + + #include <QIcon> + #include <QColor> ++#include <QVariant> + + namespace Peony { + +@@ -290,6 +291,11 @@ public: + return m_mime_type_string.contains("djvu"); + } + ++ bool isTextFile(){ ++ //纯文本文档文件太多,全都都生成缩略图会影响效率,先只限定txt文件类型 ++ return m_mime_type_string.contains("text/plain") && m_uri.endsWith(".txt"); ++ } ++ + bool isVideoFile(); + + bool isAudioFile(); +@@ -302,6 +308,12 @@ public: + // 是否禁止执行程序 + bool isExecDisable(); + ++ bool isHiddenFile(){ ++ if(m_is_hidden || displayName().startsWith(".")){ ++ return true; ++ } ++ return false; ++ } + AccessFlags accesses() { + auto flags = AccessFlags(); + #if (QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)) +@@ -351,6 +363,13 @@ public: + FileInfo &operator=(const FileInfo &other); + QString updateIconName(const QString& uri, const QString& iconName) const; + ++ bool isExistTargetOfSymlink() const; ++ ++ /** ++ * Obtaining the correct icon for a file through more methods ++ */ ++ QIcon getIcon(); ++ + Q_SIGNALS: + void updated(); + +@@ -363,8 +382,8 @@ private: + bool m_is_remote = false; + bool m_is_symbol_link = false; + bool m_is_virtual = false; +- + bool m_is_loaded = false; ++ bool m_is_hidden = false; + + QString m_display_name = nullptr; + QString m_desktop_name = nullptr; +diff --git a/libpeony-qt/file-infos-job.cpp b/libpeony-qt/file-infos-job.cpp +index 6f0acb9..04c9da4 100644 +--- a/libpeony-qt/file-infos-job.cpp ++++ b/libpeony-qt/file-infos-job.cpp +@@ -29,6 +29,7 @@ + #include "file-label-model.h" + + #include "emblem-provider.h" ++#include "file-utils.h" + + #include <gio/gdesktopappinfo.h> + #include <global-settings.h> +@@ -146,9 +147,14 @@ std::shared_ptr<FileInfo> FileInfosJob::queryFileDisplayName(std::shared_ptr<Fil + + info->m_display_name = QString (g_file_info_get_display_name(new_info)); + info->m_finalDisplayName = info->getFinalDisplayName(); +- if (info->isDesktopFile()) { ++ if (info->uri().endsWith(".desktop")) { + info->m_desktop_name = info->displayName(); + QUrl url = info->uri(); ++ if (url.scheme() == "trash" && info->targetUri() != "") ++ { ++ url = QUrl(info->targetUri()); ++ info->setProperty("enable_trash_target", true); ++ } + GDesktopAppInfo *desktop_info = g_desktop_app_info_new_from_filename(url.path().toUtf8()); + if (!desktop_info) { + //info->m_mutex.unlock(); +@@ -164,18 +170,23 @@ std::shared_ptr<FileInfo> FileInfosJob::queryFileDisplayName(std::shared_ptr<Fil + auto key = "Name[" + QLocale::system().name() + "]"; + g_autofree gchar* string = g_desktop_app_info_get_string(desktop_info, key.toUtf8().constData()); + #endif +- qDebug() << "get name string:"<<string <<info->uri()<<info->displayName(); +- QString path = "/usr/share/applications/" + info->displayName(); +- if(QFileInfo::exists(url.path().toUtf8()) && QFileInfo::exists(path)) ++ /* ++ * Show the application name whenever you have executable permissions. ++ * The /usr/share/applications/ path shows all application names. ++ * All other cases only show the file name. ++ */ ++ if(info->canExecute() || (QFileInfo::exists(url.path().toUtf8()) && url.path().startsWith("/usr/share/applications/"))) + { +- url = path; +- desktop_info = g_desktop_app_info_new_from_filename(url.path().toUtf8()); +- string = g_desktop_app_info_get_locale_string(desktop_info, "Name"); + info->m_display_name = string; + } +- else{ ++ else if (FileUtils::isExecuteTargetUribyTrashUri(url, info.get())) ++ { + info->m_display_name = string; + } ++ else ++ { ++ info->m_display_name = QString(g_file_info_get_display_name(new_info)); ++ } + info->m_finalDisplayName = info->getFinalDisplayName(); + + g_object_unref(desktop_info); +@@ -211,7 +222,11 @@ std::shared_ptr<FileInfo> FileInfosJob::refreshInfoContents(std::shared_ptr<File + info = queryFileType(info, new_info); + + /* 获取info的G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN字段的值 */ +- info->setProperty(G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN, g_file_info_get_attribute_boolean(new_info, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN)); ++ info->m_is_hidden = g_file_info_get_attribute_boolean(new_info, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN); ++ if (info->m_is_hidden) { ++ qDebug() << info->uri() << "is hidden"; ++ } ++ info->setProperty(G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN, info->m_is_hidden); + + info->m_is_symbol_link = g_file_info_get_attribute_boolean(new_info, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK); + if (g_file_info_has_attribute(new_info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) { +diff --git a/libpeony-qt/file-launcher/file-lauch-dialog.cpp b/libpeony-qt/file-launcher/file-lauch-dialog.cpp +index 0876147..3891d08 100644 +--- a/libpeony-qt/file-launcher/file-lauch-dialog.cpp ++++ b/libpeony-qt/file-launcher/file-lauch-dialog.cpp +@@ -55,7 +55,15 @@ ActionGlobalData *FileLauchDialog::actionGlobalData = nullptr; + FileLauchDialog::FileLauchDialog(const QString &uri, QWidget *parent) : QDialog(parent) + { + this->getFIleInfo(uri); +- QIcon windowicon = QIcon::fromTheme(m_info->iconName()); ++ /** ++ * @bug #278567: [File Manager] There is no icon in the upper left corner of the Unknown Open Method page ++ * ++ * Obtaining the correct icon for a file through more methods ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-10-31 ++ */ ++ QIcon windowicon = m_info->getIcon(); + setWindowIcon(windowicon); + this->setWindowFlags(windowFlags() & ~Qt::WindowMinMaxButtonsHint ); + init(uri); +@@ -238,7 +246,10 @@ void FileLauchDialog::initFloorTwo(const QString &uri) + m_view->setAlternatingRowColors(true); + m_view->setIconSize(QSize(40, 40)); + m_view->setStyleSheet("QListWidget::Item{margin-left:22px;}"); +- ++ /** ++ * Default unfocusing when opening the “More Apps” pop-up window ++ */ ++ m_view->setFocusPolicy(Qt::NoFocus); + connect(m_view, &QListWidget::currentItemChanged, [=](QListWidgetItem *current) { + if (!current) + return ; +@@ -254,7 +265,10 @@ void FileLauchDialog::initFloorTwo(const QString &uri) + + this->m_layout->addWidget(floor2); + m_check_box = new QCheckBox(tr("Always open the %1%2 file with this application").arg(".").arg(m_info.get()->displayName().split(".").last())); +- ++ /** ++ * Initialize the checkbox state ++ */ ++ m_check_box->setChecked(m_info->property("isAlwaysOpenWithThisApp").toBool()); + this->m_layout->addWidget(m_check_box); + if(!m_info.get()->displayName().contains(".")) + m_check_box->setVisible(false); +@@ -357,6 +371,15 @@ void FileLauchDialog::initFloorFour() + + connect(cancelButton, &QPushButton::clicked, this, &QMainWindow::close); + connect(okButton, &QPushButton::clicked, this, [=]() { ++ /** ++ * @bug #278503: [File Manager] Re-enter the open mode screen without checking “Always open files with this application”. ++ * ++ * Record the checkbox selection of m_check_box in the property attribute of m_info ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-10-31 ++ */ ++ m_info->setProperty("isAlwaysOpenWithThisApp", m_check_box->isChecked()); + bool doLaunch = true; + auto var = this->property("doLaunch"); + if (var.isValid() && !var.toBool()) { +diff --git a/libpeony-qt/file-launcher/file-launch-action.cpp b/libpeony-qt/file-launcher/file-launch-action.cpp +index d1e0115..55bf48b 100644 +--- a/libpeony-qt/file-launcher/file-launch-action.cpp ++++ b/libpeony-qt/file-launcher/file-launch-action.cpp +@@ -412,9 +412,31 @@ void FileLaunchAction::lauchFileAsync(bool forceWithArg, bool skipDialog) + + if (isDesktopFileAction() && !forceWithArg) { + #if GLIB_CHECK_VERSION(2, 60, 0) ++ // send startup info to kwindowsystem ++ quint32 timeStamp = QX11Info::isPlatformX11() ? QX11Info::appUserTime() : 0; ++ KStartupInfoId* startInfoId = new KStartupInfoId(); ++ startInfoId->initId(KStartupInfo::createNewStartupIdForTimestamp(timeStamp)); ++ startInfoId->setupStartupEnv(); ++ KStartupInfoData data; ++ data.setHostname(); ++ float scale = qApp->devicePixelRatio(); ++ QRect rect = fileInfo.get()->property("iconGeometry").toRect(); ++ rect.moveTo(rect.x()/* * scale*/, rect.y()/* * scale*/); ++#ifdef KSTARTUPINFO_HAS_SET_ICON_GEOMETRY ++ if (rect.isValid()) { ++ data.setIconGeometry(rect); ++ qDebug() << "KStartupInfoData iconGeometry:" << m_uri <<rect ; ++ } ++#endif ++ data.setLaunchedBy(getpid()); ++ ++ KStartupInfo::sendStartup(*startInfoId, data); ++ ++ auto context = getAppContext(this); ++ g_signal_connect(context, "launched", G_CALLBACK(contextLaunchedCallback), startInfoId); + g_app_info_launch_uris_async(m_app_info, nullptr, +- nullptr, nullptr, +- nullptr, nullptr); ++ context, nullptr, ++ GAsyncReadyCallback(launchAsyncCallback), startInfoId); + #else + g_app_info_launch_uris(m_app_info, nullptr, nullptr, nullptr); + #endif +diff --git a/libpeony-qt/file-launcher/file-launch-manager.cpp b/libpeony-qt/file-launcher/file-launch-manager.cpp +index 0f92722..64d4f1c 100644 +--- a/libpeony-qt/file-launcher/file-launch-manager.cpp ++++ b/libpeony-qt/file-launcher/file-launch-manager.cpp +@@ -169,6 +169,13 @@ FileLaunchAction *FileLaunchManager::getDefaultAction(const QString &uri) + action->setProperty("isMdmApp", isMdmApp); + g_object_unref(info); + ++ //fix bug#312687, install 360 compress software, double click zip file wrong issue ++ if (action->getAppInfoName().isEmpty()){ ++ auto recommentActions = getRecommendActions(uri); ++ if (recommentActions.length() > 0) ++ action = recommentActions.first(); ++ } ++ + return action; + } + } +@@ -262,7 +269,7 @@ void FileLaunchManager::openSync(const QString &uri, bool forceWithArg, bool ski + QString tmp = uri; + auto targetUri = FileUtils::getTargetUri(uri); + if (targetUri.isNull()) { +- tmp = targetUri; ++ tmp = uri; + } + auto action = getDefaultAction(tmp); + action->lauchFileSync(forceWithArg, skipDialog); +@@ -277,6 +284,14 @@ void FileLaunchManager::openAsync(const QString &uri, bool forceWithArg, bool sk + if (!targetUri.isNull()) { + tmp = targetUri; + qDebug()<<"open async"<<targetUri; ++ // fix #256959 ++ // note: 当软链接指向的文件信息有缓存时,应该更新软链接指向文件信息后再进行打开操作 ++ // 否则可能会导致打开文件异常报错 ++ auto info = FileInfo::fromUri(tmp); ++ if (!info->isEmptyInfo()) { ++ FileInfoJob job(info); ++ job.querySync(); ++ } + } + auto action = getDefaultAction(tmp); + action->lauchFileAsync(forceWithArg, skipDialog); +@@ -309,6 +324,39 @@ bool FileLaunchManager::isGlibForceUsePortal() + return glib_force_use_portal; + } + ++void FileLaunchManager::openFilesByDefaultApplications(const QStringList &files) ++{ ++ QMap<QString, QStringList> fileMap; ++ for (auto uri : files) { ++ QString defaultAppName = Peony::FileLaunchManager::getDefaultAction(uri)->getAppInfoName(); ++ QStringList list; ++ if (fileMap.contains(defaultAppName)) { ++ list = fileMap[defaultAppName]; ++ list << uri; ++ fileMap.insert(defaultAppName, list); ++ } else { ++ list << uri; ++ fileMap.insert(defaultAppName, list); ++ } ++ } ++ if(!fileMap.empty()) { ++ QMap<QString, QStringList>::iterator iter = fileMap.begin(); ++ while (iter != fileMap.end()) ++ { ++ Peony::FileLaunchManager::openAsync(iter.value()); ++ iter++; ++ } ++ } ++} ++ ++FileLaunchAction *FileLaunchManager::getPeonyAction(const QString &uri) ++{ ++ auto app_info = g_desktop_app_info_new_from_filename("/usr/share/applications/peony.desktop"); ++ auto action = new FileLaunchAction(uri, G_APP_INFO(app_info), true); ++ return action; ++} ++ ++ + void FileLaunchManager::setDefaultLauchAction(const QString &uri, FileLaunchAction *action) + { + //FIXME: replace BLOCKING api in ui thread. +diff --git a/libpeony-qt/file-launcher/file-launch-manager.h b/libpeony-qt/file-launcher/file-launch-manager.h +index 23e2d85..fa34458 100644 +--- a/libpeony-qt/file-launcher/file-launch-manager.h ++++ b/libpeony-qt/file-launcher/file-launch-manager.h +@@ -62,6 +62,10 @@ public: + static void openAsync(const QStringList &files, bool forceWithArg = false, bool skipDialog = true); + static bool isGlibForceUsePortal(); + ++ static void openFilesByDefaultApplications(const QStringList &files); ++ ++ static FileLaunchAction *getPeonyAction(const QString &uri); ++ + private: + explicit FileLaunchManager(QObject *parent = nullptr); + }; +diff --git a/libpeony-qt/file-operation/create-template-operation.cpp b/libpeony-qt/file-operation/create-template-operation.cpp +index cdc75a1..4fb4e85 100644 +--- a/libpeony-qt/file-operation/create-template-operation.cpp ++++ b/libpeony-qt/file-operation/create-template-operation.cpp +@@ -39,7 +39,10 @@ using namespace Peony; + + void CreateTemplateOperation::handleDuplicate(const QString &uri) + { +- m_target_uri = m_dest_dir_uri + "/" + FileUtils::handleDuplicateName(uri); ++ //fix task#372795, use file name to process duplicate file issue, avoid path has special char effect result ++ QString baseName = uri.split("/").last(); ++ qDebug() << "CreateTemplateOperation handleDuplicate:"<<baseName<<m_dest_dir_uri; ++ m_target_uri = m_dest_dir_uri + "/" + FileUtils::handleDuplicateName(baseName); + } + + CreateTemplateOperation::CreateTemplateOperation(const QString &destDirUri, Type type, const QString &templateName, QObject *parent) : FileOperation(parent) +@@ -71,15 +74,37 @@ void CreateTemplateOperation::run() + { + Q_EMIT operationStarted(); + Q_EMIT operationPrepared(); ++ ++ if (queryDirIsReadOnlyFS(m_dest_dir_uri)) { ++ FileOperationError except; ++ except.dlgType = ED_WARNING; ++ except.errorType = ET_GIO; ++ except.srcUri = m_src_uri; ++ except.destDirUri = m_dest_dir_uri; ++ except.op = FileOpCreateTemp; ++ except.title = tr("File create error"); ++ QUrl srcUrl(except.srcUri); ++ except.errorStr = tr("Can not create %1: Read-only mode, can not write-in").arg(srcUrl.fileName()); ++ errored(except); ++ setHasError(true); ++ Q_EMIT operationFinished(); ++ return; ++ } ++ ++ uint retryTime = 0; + switch (m_type) { + case EmptyFile: { + m_target_uri = m_dest_dir_uri + "/" + tr("NewFile") + ".txt"; + retry_create_empty_file: ++ retryTime++; + GError *err = nullptr; + GFileOutputStream *newFile = g_file_create(wrapGFile(g_file_new_for_uri(FileUtils::urlEncode(m_target_uri).toUtf8())).get()->get(), G_FILE_CREATE_NONE, nullptr, &err); + if (err) { ++ //fix task#372795, create same name file in phone failed issue + FileOperationError except; +- if (err->code == G_IO_ERROR_EXISTS) { ++ qDebug() << "Create EmptyFile errCode:"<<err->code<<" m_target_uri:"<<m_target_uri<<retryTime; ++ if (retryTime <= 300 && (err->code == G_IO_ERROR_EXISTS || ++ (m_target_uri.startsWith("mtp://") && err->code == G_IO_ERROR_FAILED))) { + g_error_free(err); + handleDuplicate(m_target_uri); + goto retry_create_empty_file; +@@ -104,13 +129,17 @@ retry_create_empty_file: + case EmptyFolder: { + m_target_uri = m_dest_dir_uri + "/" + tr("NewFolder"); + retry_create_empty_folder: ++ retryTime++; + GError *err = nullptr; + g_file_make_directory(wrapGFile(g_file_new_for_uri(FileUtils::urlEncode(m_target_uri).toUtf8())).get()->get(), + nullptr, + &err); + if (err) { + // todo: Allow user naming +- if (err->code == G_IO_ERROR_EXISTS) { ++ //fix task#372795, create same name file in phone failed issue ++ qDebug() << "Create EmptyFolder errCode:"<<err->code<<" m_target_uri:"<<m_target_uri<<retryTime; ++ if (retryTime <= 300 && (err->code == G_IO_ERROR_EXISTS || ++ (m_target_uri.startsWith("mtp://") && err->code == G_IO_ERROR_FAILED))) { + g_error_free(err); + handleDuplicate(m_target_uri); + goto retry_create_empty_folder; +@@ -133,7 +162,8 @@ retry_create_empty_folder: + } + case Template: { + retry_create_template: +- qDebug() << "create tmp"; ++ retryTime++; ++ qDebug() << "create tmp file such as wps empty file:"<<retryTime; + GError *err = nullptr; + g_file_copy(wrapGFile(g_file_new_for_uri(FileUtils::urlEncode(m_src_uri).toUtf8())).get()->get(), + wrapGFile(g_file_new_for_uri(m_target_uri.toUtf8())).get()->get(), +@@ -144,7 +174,10 @@ retry_create_template: + &err); + if (err) { + setHasError(true); +- if (err->code == G_IO_ERROR_EXISTS) { ++ qDebug() << "Create Template errCode:"<<err->code<<" m_target_uri:"<<m_target_uri; ++ //fix task#372795, create same name file in phone failed issue ++ if (retryTime <= 300 && (err->code == G_IO_ERROR_EXISTS || ++ (m_target_uri.startsWith("mtp://") && err->code == G_IO_ERROR_FAILED))) { + g_error_free(err); + handleDuplicate(m_target_uri); + goto retry_create_template; +@@ -212,12 +245,17 @@ retry_create_template: + //needSync = true; + + if (needSync) { +- auto path = g_file_get_path(dest_dir_file); ++ char *path = g_file_get_path(dest_dir_file); + if (path) { + operationStartSnyc(); + QProcess p; +- p.start(QString("/usr/bin/sync -f '%1'").arg(path)); ++ p.start(QString("/usr/bin/sync -f %1").arg(path)); + p.waitForFinished(-1); ++ if (p.exitCode() == 0) { ++ qDebug() << "sync completed successfully"; ++ } else { ++ qDebug() << "sync failed with exit code:" << p.exitCode(); ++ } + g_free(path); + } + } +diff --git a/libpeony-qt/file-operation/file-copy-operation.cpp b/libpeony-qt/file-operation/file-copy-operation.cpp +index d756404..8669cd5 100644 +--- a/libpeony-qt/file-operation/file-copy-operation.cpp ++++ b/libpeony-qt/file-operation/file-copy-operation.cpp +@@ -44,19 +44,35 @@ using namespace Peony; + + static void handleDuplicate(FileNode *node) + { +- node->setDestFileName(FileUtils::handleDuplicateName(node->destBaseName())); ++ if (node->isFolder()) { ++ node->setDestFileName(FileUtils::handleFolderName(node->destBaseName())); ++ } else { ++ node->setDestFileName(FileUtils::handleDuplicateName(node->destBaseName())); ++ } + } + + FileCopyOperation::FileCopyOperation(QStringList sourceUris, QString destDirUri, QObject *parent) : FileOperation (parent) + { ++ //origin code from dj, for sangfor clound project change ++ //fix bug#249783, copy absolute file crash issue + for (auto u : sourceUris) { +- if (u.split ("://").length () != 2) { ++ if (u.split("://").length () > 2) { + sourceUris.removeOne (u); + } + } + + QUrl destDirUrl = Peony::FileUtils::urlEncode(destDirUri); +- QUrl firstSrcUrl = Peony::FileUtils::urlEncode(sourceUris.first()); ++ //fix bug#249783, copy absolute file crash issue ++ QString srcId = ""; ++ if (sourceUris.length() > 0) ++ srcId = Peony::FileUtils::urlEncode(sourceUris.first()); ++ if (! srcId.startsWith("file://") && !srcId.contains("://")) { ++ //关联bug# 261581蓝信复制失败问题,主要问题为蓝信的复制至剪切版MimeData中的url存在异常,导致url编码后是绝对路径 ++ //由于蓝信无批量复制功能且不支持拖拽,故当前只处理拷贝第一个文件 ++ srcId = "file://" + srcId; ++ sourceUris.replace(0, srcId); ++ } ++ QUrl firstSrcUrl = srcId; + if("label" == firstSrcUrl.scheme()) + { + QString scheme = firstSrcUrl.path().section("?schema=",-1,-1); +@@ -83,7 +99,7 @@ FileCopyOperation::FileCopyOperation(QStringList sourceUris, QString destDirUri, + if(uri.startsWith("label://")) + { + QUrl url(uri); +- QString scheme = url.path().section("?schema=",-1,-1); ++ QString scheme = uri.section("?schema=",-1,-1); + QString path = url.path().section("?schema=", 0, 0).section("/",2,-1); + uri = QString(scheme).append(":///").append(path); + } +@@ -115,7 +131,7 @@ ExceptionResponse FileCopyOperation::prehandle(GError *err) + case G_IO_ERROR_CANCELLED: + case G_IO_ERROR_INVALID_DATA: + case G_IO_ERROR_NOT_SUPPORTED: +- case G_IO_ERROR_PERMISSION_DENIED: ++// case G_IO_ERROR_PERMISSION_DENIED: + case G_IO_ERROR_CANT_CREATE_BACKUP: + case G_IO_ERROR_TOO_MANY_OPEN_FILES: + return Other; +@@ -237,7 +253,12 @@ fallback_retry: + if (handle_type == Other) { + switch (err->code) { + case G_IO_ERROR_EXISTS: { +- except.dlgType = ED_CONFLICT; ++ if (isDlpState()) { ++ except.dlgType = ED_WARNING; ++ except.errorStr = tr("Cannot opening file, permission denied!"); ++ } else { ++ except.dlgType = ED_CONFLICT; ++ } + Q_EMIT errored(except); + auto typeData = except.respCode; + handle_type = typeData; +@@ -314,6 +335,21 @@ fallback_retry: + setHasError(true); + node->setState(FileNode::Invalid); + node->setErrorResponse(OverWriteOne); ++ if (G_IO_ERROR_EXISTS == err->code) { ++ g_autoptr(GFileInfo) info = g_file_query_info(destFile.get()->get() ++ , G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ++ , G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); ++ if (info) { ++ if (G_FILE_TYPE_SYMBOLIC_LINK == g_file_info_get_file_type(info)) { ++ g_file_delete(destFile.get()->get(), nullptr, &error); ++ if (error) { ++ qDebug() << error->code << error->message; ++ } else { ++ goto fallback_retry; ++ } ++ } ++ } ++ } + if (!m_is_udf_warning && m_is_udf_burn_work) { + auto result = udfCopyWarningDialog(); + if (Cancel == result) { +@@ -339,6 +375,21 @@ fallback_retry: + node->setState(FileNode::Invalid); + node->setErrorResponse(OverWriteOne); + m_prehandle_hash.insert(err->code, OverWriteOne); ++ if (G_IO_ERROR_EXISTS == err->code) { ++ g_autoptr(GFileInfo) info = g_file_query_info(destFile.get()->get() ++ , G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ++ , G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); ++ if (info) { ++ if (G_FILE_TYPE_SYMBOLIC_LINK == g_file_info_get_file_type(info)) { ++ g_file_delete(destFile.get()->get(), nullptr, &error); ++ if (error) { ++ qDebug() << error->code << error->message; ++ } else { ++ goto fallback_retry; ++ } ++ } ++ } ++ } + if (!m_is_udf_warning && m_is_udf_burn_work) { + m_is_udf_warning = true; + auto result = udfCopyWarningDialog(); +@@ -466,25 +517,6 @@ fallback_retry: + } + } else { + node->setState(FileNode::Handled); +- // if copy sucessed, flush all data +- g_autoptr(GFile) destFile = g_file_new_for_uri(destFileUri.toUtf8().constData()); +- if (g_file_query_exists(destFile, nullptr)) { +- // copy file attribute +- // It is possible that some file systems do not support file attributes +- g_autoptr(GFile) srcFile = g_file_new_for_uri(srcUri.toUtf8().constData()); +- g_file_copy_attributes(srcFile, destFile, G_FILE_COPY_ALL_METADATA, nullptr, &err); +- if (nullptr != err) { +- qWarning() <<destFileUri<<"copy attribute error:" << err->code << " --- " << err->message; +- g_error_free(err); +- err = nullptr; +- } +- QList<int> labelIds = FileLabelModel::getGlobalModel()->getFileLabelIds(srcUri); +- for(auto &labelId: labelIds){ +- if(labelId <= 0) +- continue; +- FileLabelModel::getGlobalModel()->addLabelToFile(destFileUri, labelId); +- } +- } + } + + if (SaveOne == node->responseType() || SaveAll == node->responseType()) { +@@ -506,6 +538,14 @@ fallback_retry: + m_parent_flags, + nullptr, + &error); ++ ++ /* 文件夹copy成功后,如有标记需添加到全局对应的标记页面 */ ++ QList<int> labelIds = FileLabelModel::getGlobalModel()->getFileLabelIds(srcUri); ++ for(auto &labelId: labelIds){ ++ if(labelId <= 0) ++ continue; ++ FileLabelModel::getGlobalModel()->addLabelToFile(destFileUri, labelId); ++ }//end + } + + if (error) { +@@ -638,7 +678,12 @@ fallback_retry: + if (handle_type == Other) { + switch (err->code) { + case G_IO_ERROR_EXISTS: { +- except.dlgType = ED_CONFLICT; ++ if (isDlpState()) { ++ except.dlgType = ED_WARNING; ++ except.errorStr = tr("Cannot opening file, permission denied!"); ++ } else { ++ except.dlgType = ED_CONFLICT; ++ } + Q_EMIT errored(except); + auto typeData = except.respCode; + handle_type = typeData; +@@ -1016,8 +1061,59 @@ void FileCopyOperation::run() + } + #endif + ++ bool writeable = true; ++ if (queryDirIsReadOnlyFS(m_dest_dir_uri, false, m_is_udf_burn_work, &writeable)) { ++ FileOperationError except; ++ except.dlgType = ED_WARNING; ++ except.errorType = ET_GIO; ++ except.srcUri = m_source_uris.isEmpty()? nullptr: m_source_uris.first(); ++ except.destDirUri = m_dest_dir_uri; ++ except.op = FileOpCopy; ++ except.title = tr("File copy error"); ++ QUrl srcUrl(except.srcUri); ++ QUrl destUrl(except.destDirUri); ++ except.errorStr = tr("Can not copy %1 to %2: Read-only mode, can not write-in").arg(srcUrl.fileName()).arg(destUrl.fileName()); ++ errored(except); ++ setHasError(true); ++ Q_EMIT operationFinished(); ++ return; ++ } else if (!writeable) { ++ FileOperationError except; ++ except.dlgType = ED_WARNING; ++ except.errorType = ET_GIO; ++ except.srcUri = m_source_uris.isEmpty()? nullptr: m_source_uris.first(); ++ except.destDirUri = m_dest_dir_uri; ++ except.op = FileOpCopy; ++ except.title = tr("File copy error"); ++ QUrl srcUrl(except.srcUri); ++ QUrl destUrl(except.destDirUri); ++ except.errorStr = tr("Can not copy %1 to %2: Permission denied").arg(srcUrl.fileName()).arg(destUrl.fileName()); ++ errored(except); ++ setHasError(true); ++ Q_EMIT operationFinished(); ++ return; ++ } ++ + Q_EMIT operationRequestShowWizard(); + ++// if(m_dest_dir_uri.startsWith("mtp://") || m_dest_dir_uri.startsWith("gphoto2://")) { ++// int usbSafeMode = getUsbSafeMode(); ++// if (usbSafeMode != 0) { ++// FileOperationError except; ++// except.dlgType = ED_WARNING; ++// except.errorType = ET_GIO; ++// except.srcUri = m_source_uris.isEmpty()? nullptr: m_source_uris.first(); ++// except.destDirUri = m_dest_dir_uri; ++// except.op = FileOpCopy; ++// except.title = tr("File copy error"); ++// except.errorStr = tr("open file %1 error: Read-only file system").arg(m_dest_dir_uri.split("://").last()); ++// errored(except); ++// setHasError(true); ++// Q_EMIT operationFinished(); ++// return; ++// } ++// } ++ + goffset *total_size = new goffset(0); + + QList<FileNode*> nodes; +@@ -1039,6 +1135,7 @@ void FileCopyOperation::run() + } + node->findChildrenRecursively(); + node->computeTotalSize(total_size); ++ m_total_count += m_reporter->getTotalCount(); + nodes << node; + } + +@@ -1066,7 +1163,7 @@ void FileCopyOperation::run() + FileOperationError except; + QString name; + if (storage.rootPath() == "/") { +- name = tr("File System"); ++ name = tr("System Disk"); + } else if (storage.rootPath() == "/data") { + name = tr("Data"); + } else { +@@ -1104,6 +1201,10 @@ void FileCopyOperation::run() + } + } + ++ if (m_total_count <= 500 && m_total_size < 300 * 1024 * 1024) { ++ syncDestUri(m_dest_dir_uri); ++ } ++ + //comment to fix bug#177163, copy and paste file has error and play success sound issue + //copy operation has finished, no need reset flag, keep the same with move-operation + //setHasError(false); +@@ -1123,6 +1224,8 @@ void FileCopyOperation::run() + burnUris.removeOne(node->uri()); + break; + case BackupOne: ++ case TruncateOne: ++ case RenameOne: + burnUris.replaceInStrings(node->uri(), node->destUri()); + break; + default: +@@ -1134,8 +1237,10 @@ void FileCopyOperation::run() + } + m_info->m_dest_uris = m_info->m_node_map.values(); + nodes.clear(); ++ + #ifdef KY_UDF_BURN + if (mHelper->isUnixCDDevice() && !isCancelled()) { ++ Q_EMIT operationUdfBurnRunning(true); + if(!mHelper->discWriteOperation(burnUris, m_dest_dir_uri)) { + FileOperationError except; + except.errorType = ET_CUSTOM; +@@ -1148,6 +1253,7 @@ void FileCopyOperation::run() + Q_EMIT errored(except); + } + m_is_udf_burn_work = false; ++ Q_EMIT operationUdfBurnRunning(false); + } else { + if (m_is_udf_burn_work) { + m_is_udf_burn_work = false; +@@ -1368,6 +1474,46 @@ bool FileCopyOperation::saveAsOtherPath() + return true; + } + ++int FileCopyOperation::getUsbSafeMode() ++{ ++ return 0; ++ ++ QFile file("/sys/devices/platform/hw_trans_bios_variable/usb_safe_mode"); ++ if (!file.exists()) { ++ qDebug() << "The file does not exist"; ++ return 0; ++ } ++ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { ++ qWarning() << "Error: Unable to open file"; ++ return 0; ++ } ++ // 读取文件内容 ++ QTextStream in(&file); ++ QString content = in.readLine(); ++ int status = content.toInt(); ++ file.close(); ++ return status; ++} ++ ++bool FileCopyOperation::isDlpState() ++{ ++ if (!m_dest_dir_uri.startsWith("file:///media/")) { ++ return false; ++ } ++ QFile file("/sys/kernel/security/dlp/usb_check_status"); ++ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { ++ qWarning() << "Error: Unable to open file"; ++ return false; ++ } ++ // 读取文件内容 ++ QTextStream in(&file); ++ QString content = in.readLine(); ++ int status = content.toInt(); ++ bool result = (status == 1); ++ file.close(); ++ return result; ++} ++ + void FileCopyOperation::cancel() + { + setHasError(true); +diff --git a/libpeony-qt/file-operation/file-copy-operation.h b/libpeony-qt/file-operation/file-copy-operation.h +index 3dd902c..ede93e6 100644 +--- a/libpeony-qt/file-operation/file-copy-operation.h ++++ b/libpeony-qt/file-operation/file-copy-operation.h +@@ -78,6 +78,16 @@ protected: + ExceptionResponse udfCopyWarningDialog(); + + bool saveAsOtherPath(); ++ ++ /*! ++ * \brief getUsbSafeMode ++ * \return ++ * \deprecated ++ */ ++ int getUsbSafeMode(); ++ ++ bool isDlpState(); ++ + private: + /*! + * \brief m_is_duplicated_copy +diff --git a/libpeony-qt/file-operation/file-count-operation.cpp b/libpeony-qt/file-operation/file-count-operation.cpp +index 1e5c659..e7adf21 100644 +--- a/libpeony-qt/file-operation/file-count-operation.cpp ++++ b/libpeony-qt/file-operation/file-count-operation.cpp +@@ -66,7 +66,9 @@ void FileCountOperation::run() + m_reporter->setUsedInCount(true); + auto node = new FileNode(FileUtils::urlEncode(uri), nullptr, m_reporter); + node->findChildrenRecursively(); +- nodes<<node; ++ //add skip some uri function for calculate /data/* size ++ if (m_skip_uris.length() > 0 && ! m_skip_uris.contains(node->uri())) ++ nodes<<node; + } + if (!this->isCancelled()) { + if (!m_count_root) { +diff --git a/libpeony-qt/file-operation/file-count-operation.h b/libpeony-qt/file-operation/file-count-operation.h +index 00edb78..3582f78 100644 +--- a/libpeony-qt/file-operation/file-count-operation.h ++++ b/libpeony-qt/file-operation/file-count-operation.h +@@ -45,6 +45,10 @@ public: + total_size = m_total_size; + } + ++ void setSkipUris(QStringList uris) { ++ m_skip_uris = uris; ++ } ++ + Q_SIGNALS: + void countDone(quint64 file_count, quint64 hidden_file_count, quint64 total_size); + +@@ -54,6 +58,7 @@ public Q_SLOTS: + private: + FileNodeReporter *m_reporter = nullptr; + QStringList m_uris; ++ QStringList m_skip_uris; + + quint64 m_file_count = 0; + quint64 m_hidden_file_count = 0; +diff --git a/libpeony-qt/file-operation/file-delete-operation.cpp b/libpeony-qt/file-operation/file-delete-operation.cpp +index bcffee0..23d92d8 100644 +--- a/libpeony-qt/file-operation/file-delete-operation.cpp ++++ b/libpeony-qt/file-operation/file-delete-operation.cpp +@@ -193,8 +193,13 @@ void FileDeleteOperation::run() + if (! path.isEmpty()) { + operationStartSnyc(); + QProcess p; +- p.start(QString("/usr/bin/sync -f '%1'").arg(path)); ++ p.start(QString("/usr/bin/sync -f %1").arg(path)); + p.waitForFinished(-1); ++ if (p.exitCode() == 0) { ++ qDebug() << "sync completed successfully"; ++ } else { ++ qDebug() << "sync failed with exit code:" << p.exitCode(); ++ } + } + } + +diff --git a/libpeony-qt/file-operation/file-move-operation.cpp b/libpeony-qt/file-operation/file-move-operation.cpp +index 7aac6ab..99b9e87 100644 +--- a/libpeony-qt/file-operation/file-move-operation.cpp ++++ b/libpeony-qt/file-operation/file-move-operation.cpp +@@ -41,13 +41,19 @@ using namespace Peony; + + static void handleDuplicate(FileNode *node) + { +- node->setDestFileName(FileUtils::handleDuplicateName(node->destBaseName())); ++ if (node->isFolder()) { ++ node->setDestFileName(FileUtils::handleFolderName(node->destBaseName())); ++ } else { ++ node->setDestFileName(FileUtils::handleDuplicateName(node->destBaseName())); ++ } + } + + FileMoveOperation::FileMoveOperation(QStringList sourceUris, QString destDirUri, QObject *parent) : FileOperation (parent) + { ++ //origin code from dj, for sangfor clound project change ++ //fix bug#249783, copy absolute file crash issue + for (auto u : sourceUris) { +- if (u.split("://").length() != 2) { ++ if (u.split("://").length() > 2) { + sourceUris.removeOne (u); + } + } +@@ -65,12 +71,21 @@ FileMoveOperation::FileMoveOperation(QStringList sourceUris, QString destDirUri, + m_dest_dir_uri = FileUtils::urlEncode(destDirUri); + m_info = std::make_shared<FileOperationInfo>(sourceUris, destDirUri, FileOperationInfo::Move); + +- QString srcId = FileUtils::getFileSystemId(m_src_uris.first()); ++ //fix bug#249783, copy absolute file crash issue ++ QString srcId = ""; ++ if (m_src_uris.length() > 0) ++ srcId = FileUtils::getFileSystemId(m_src_uris.first()); ++ ++ //comment to fix move file to other path, when has same file, not delete origin file issue ++// if (! srcId.startsWith("file://") && !srcId.contains("://")) ++// srcId = "file://" + srcId; + QString destId = FileUtils::getFileSystemId(m_dest_dir_uri); + if (srcId.length() > 0 && srcId == destId) + m_is_same_fs = true; + else + m_is_same_fs = false; ++ ++ m_reporter = new FileNodeReporter; + } + + FileMoveOperation::~FileMoveOperation() +@@ -211,7 +226,6 @@ void FileMoveOperation::move() + QList<FileNode*> errNode; + + GError *err = nullptr; +- m_total_count = m_src_uris.count(); + auto destDir = wrapGFile(g_file_new_for_uri(m_dest_dir_uri.toUtf8().constData())); + + // file move +@@ -219,7 +233,7 @@ void FileMoveOperation::move() + if (isCancelled()) + return; + +- auto node = new FileNode(srcUri, nullptr, nullptr); ++ auto node = new FileNode(srcUri, nullptr, m_reporter); + node->setState(FileNode::Handling); + + auto srcFile = wrapGFile(g_file_new_for_uri(srcUri.toUtf8().constData())); +@@ -275,9 +289,9 @@ void FileMoveOperation::move() + for (auto eNode : errNode) { + if (isCancelled()) + return; +- + eNode->findChildrenRecursively(); + eNode->computeTotalSize(total_size); ++ m_total_count += m_reporter->getTotalCount(); + if(eNode->isFolder()) + hasFolder = true; + } +@@ -299,7 +313,7 @@ void FileMoveOperation::move() + FileOperationError except; + QString name; + if (storage.rootPath() == "/") { +- name = tr("File System"); ++ name = tr("System Disk"); + } else if (storage.rootPath() == "/data") { + name = tr("Data"); + } else { +@@ -348,6 +362,11 @@ void FileMoveOperation::move() + } + + operationStartSnyc(); ++ if (m_total_count <= 500 && m_total_size < 300 * 1024 * 1024) { ++ syncDestUri(m_dest_dir_uri); ++ } ++ ++ + + #if 0 + for (auto file : nodes) { +@@ -583,6 +602,8 @@ void FileMoveOperation::move() + m_burn_uris.removeOne(file->uri()); + break; + case BackupOne: ++ case TruncateOne: ++ case RenameOne: + m_burn_uris.replaceInStrings(file->uri(), file->destUri()); + break; + default: +@@ -874,7 +895,12 @@ fallback_retry: + auto typeData = Invalid; + switch (err->code) { + case G_IO_ERROR_EXISTS: { +- except.dlgType = ED_CONFLICT; ++ if (isDlpState()) { ++ except.dlgType = ED_WARNING; ++ except.errorStr = tr("Cannot opening file, permission denied!"); ++ } else { ++ except.dlgType = ED_CONFLICT; ++ } + Q_EMIT errored(except); + typeData = except.respCode; + break; +@@ -955,6 +981,21 @@ fallback_retry: + node->setState(FileNode::Invalid); + node->setErrorResponse(OverWriteOne); + setHasError(true); ++ if (G_IO_ERROR_EXISTS == err->code) { ++ g_autoptr(GFileInfo) info = g_file_query_info(destFile.get()->get() ++ , G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ++ , G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); ++ if (info) { ++ if (G_FILE_TYPE_SYMBOLIC_LINK == g_file_info_get_file_type(info)) { ++ g_file_delete(destFile.get()->get(), nullptr, &error); ++ if (error) { ++ qDebug() << error->code << error->message; ++ } else { ++ goto fallback_retry; ++ } ++ } ++ } ++ } + if (!m_is_udf_warning && m_is_udf_burn_work) { + auto result = udfCopyWarningDialog(); + if (Cancel == result) { +@@ -995,6 +1036,21 @@ fallback_retry: + node->setErrorResponse(OverWriteOne); + setHasError(true); + m_prehandle_hash.insert(err->code, OverWriteOne); ++ if (G_IO_ERROR_EXISTS == err->code) { ++ g_autoptr(GFileInfo) info = g_file_query_info(destFile.get()->get() ++ , G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET ++ , G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); ++ if (info) { ++ if (G_FILE_TYPE_SYMBOLIC_LINK == g_file_info_get_file_type(info)) { ++ g_file_delete(destFile.get()->get(), nullptr, &error); ++ if (error) { ++ qDebug() << error->code << error->message; ++ } else { ++ goto fallback_retry; ++ } ++ } ++ } ++ } + if (!m_is_udf_warning && m_is_udf_burn_work) { + m_is_udf_warning = true; + auto result = udfCopyWarningDialog(); +@@ -1228,7 +1284,12 @@ fallback_retry: + auto typeData = Invalid; + switch (err->code) { + case G_IO_ERROR_EXISTS: { +- except.dlgType = ED_CONFLICT; ++ if (isDlpState()) { ++ except.dlgType = ED_WARNING; ++ except.errorStr = tr("Cannot opening file, permission denied!"); ++ } else { ++ except.dlgType = ED_CONFLICT; ++ } + Q_EMIT errored(except); + typeData = except.respCode; + break; +@@ -1320,6 +1381,12 @@ fallback_retry: + // node->setErrorResponse(OverWriteOne); + if (nodeErr){ + node->setErrorResponse(Invalid); ++ if (nodeErr->code == G_IO_ERROR_FAILED) { ++ except.errorCode = nodeErr->code; ++ except.errorStr = nodeErr->message; ++ except.dlgType = ED_WARNING; ++ Q_EMIT errored(except); ++ } + g_error_free(nodeErr); + }else{ + } +@@ -1363,6 +1430,12 @@ fallback_retry: + m_prehandle_hash.insert(err->code, OverWriteAll); + if (nodeErr){ + node->setErrorResponse(Invalid); ++ if (nodeErr->code == G_IO_ERROR_FAILED) { ++ except.errorCode = nodeErr->code; ++ except.errorStr = nodeErr->message; ++ except.dlgType = ED_WARNING; ++ Q_EMIT errored(except); ++ } + g_error_free(nodeErr); + }else{ + } +@@ -1389,9 +1462,8 @@ fallback_retry: + handleDuplicate(node); + node->resolveDestFileUri(m_dest_dir_uri); + } +- auto handledDestFileUri = node->resolveDestFileUri(m_dest_dir_uri); +- auto handledDestFile = wrapGFile(g_file_new_for_uri(handledDestFileUri.toUtf8())); +- if (handledDestFileUri.length() > 255 && ++ QString destFileBaseName = node->destBaseName(); ++ if (destFileBaseName.length() > 255 && + m_dest_dir_uri.startsWith(QString("file://" + QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/扩展"))) { + QString msg = tr("The file name exceeds the limit"); + Q_EMIT operationInfoMsgBox(msg); +@@ -1413,6 +1485,12 @@ fallback_retry: + fileCopy.run(); + if (nodeErr) { + node->setErrorResponse(Invalid); ++ if (nodeErr->code == G_IO_ERROR_FAILED) { ++ except.errorCode = nodeErr->code; ++ except.errorStr = nodeErr->message; ++ except.dlgType = ED_WARNING; ++ Q_EMIT errored(except); ++ } + g_error_free (nodeErr); + } else { + // 设置node的状态为handled用于后续删除原文件的流程 +@@ -1702,6 +1780,27 @@ void FileMoveOperation::run() + } + } + ++ bool writeable = true; ++ ++ //fix bug 232253,手机管控下,拖拽至对应目录直接报错 ++// if(m_dest_dir_uri.startsWith("mtp://") || m_dest_dir_uri.startsWith("gphoto2://")) { ++// int usbSafeMode = getUsbSafeMode(); ++// if (usbSafeMode != 0) { ++// FileOperationError except; ++// except.dlgType = ED_WARNING; ++// except.errorType = ET_GIO; ++// except.srcUri = m_src_uris.isEmpty()? nullptr: m_src_uris.first(); ++// except.destDirUri = m_dest_dir_uri; ++// except.op = FileOpMove; ++// except.title = tr("File move error"); ++// except.errorStr = tr("open file %1 error: Read-only file system").arg(m_dest_dir_uri.split("://").last()); ++// errored(except); ++// setHasError(true); ++// Q_EMIT operationFinished(); ++// return; ++// } ++// } ++ + #ifdef KY_UDF_BURN + std::shared_ptr<FileOperationHelper> mHelper = std::make_shared<FileOperationHelper>(m_dest_dir_uri); + if (mHelper->isUnixCDDevice()) { +@@ -1732,6 +1831,38 @@ void FileMoveOperation::run() + } + #endif + ++ if (queryDirIsReadOnlyFS(m_dest_dir_uri, false, m_is_udf_burn_work, &writeable)) { ++ FileOperationError except; ++ except.dlgType = ED_WARNING; ++ except.errorType = ET_GIO; ++ except.srcUri = m_src_uris.isEmpty()? nullptr: m_src_uris.first(); ++ except.destDirUri = m_dest_dir_uri; ++ except.op = FileOpMove; ++ except.title = tr("File move error"); ++ QUrl srcUrl(except.srcUri); ++ QUrl destUrl(except.destDirUri); ++ except.errorStr = tr("Can not move %1 to %2: Read-only mode, can not write-in").arg(srcUrl.fileName()).arg(destUrl.fileName()); ++ errored(except); ++ setHasError(true); ++ Q_EMIT operationFinished(); ++ return; ++ } else if (!writeable) { ++ FileOperationError except; ++ except.dlgType = ED_WARNING; ++ except.errorType = ET_GIO; ++ except.srcUri = m_src_uris.isEmpty()? nullptr: m_src_uris.first(); ++ except.destDirUri = m_dest_dir_uri; ++ except.op = FileOpMove; ++ except.title = tr("File move error"); ++ QUrl srcUrl(except.srcUri); ++ QUrl destUrl(except.destDirUri); ++ except.errorStr = tr("Can not move %1 to %2: Permission denied").arg(srcUrl.fileName()).arg(destUrl.fileName()); ++ errored(except); ++ setHasError(true); ++ Q_EMIT operationFinished(); ++ return; ++ } ++ + start: + if (!isValid()) { + FileOperationError except; +@@ -1769,6 +1900,7 @@ start: + end: + #ifdef KY_UDF_BURN + if (mHelper->isUnixCDDevice() && !isCancelled()) { ++ Q_EMIT operationUdfBurnRunning(true); + if(!mHelper->discWriteOperation(m_burn_uris, m_dest_dir_uri)) { + FileOperationError except; + except.errorType = ET_CUSTOM; +@@ -1781,6 +1913,7 @@ end: + Q_EMIT errored(except); + } + m_is_udf_burn_work = false; ++ Q_EMIT operationUdfBurnRunning(false); + } else { + if (m_is_udf_burn_work) { + m_is_udf_burn_work = false; +@@ -1994,6 +2127,45 @@ bool FileMoveOperation::saveAsOtherPath() + return true; + } + ++int FileMoveOperation::getUsbSafeMode() ++{ ++ return 0; ++ QFile file("/sys/devices/platform/hw_trans_bios_variable/usb_safe_mode"); ++ if (!file.exists()) { ++ qDebug() << "The file does not exist"; ++ return 0; ++ } ++ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { ++ qWarning() << "Error: Unable to open file"; ++ return 0; ++ } ++ // 读取文件内容 ++ QTextStream in(&file); ++ QString content = in.readLine(); ++ int status = content.toInt(); ++ file.close(); ++ return status; ++} ++ ++bool FileMoveOperation::isDlpState() ++{ ++ if (!m_dest_dir_uri.startsWith("file:///media/")) { ++ return false; ++ } ++ QFile file("/sys/kernel/security/dlp/usb_check_status"); ++ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { ++ qWarning() << "Error: Unable to open file"; ++ return false; ++ } ++ // 读取文件内容 ++ QTextStream in(&file); ++ QString content = in.readLine(); ++ int status = content.toInt(); ++ bool result = (status == 1); ++ file.close(); ++ return result; ++} ++ + void FileMoveOperation::cancel() + { + setHasError(true); +diff --git a/libpeony-qt/file-operation/file-move-operation.h b/libpeony-qt/file-operation/file-move-operation.h +index e26ae0a..6657d22 100644 +--- a/libpeony-qt/file-operation/file-move-operation.h ++++ b/libpeony-qt/file-operation/file-move-operation.h +@@ -102,6 +102,8 @@ public: + */ + void rollbackNodeRecursively(FileNode *node); + ++ bool isDlpState(); ++ + void run() override; + + std::shared_ptr<FileOperationInfo> getOperationInfo() override { +@@ -149,6 +151,13 @@ protected: + */ + ExceptionResponse prehandle(GError *err); + ++ /*! ++ * \brief getUsbSafeMode ++ * \return ++ * \deprecated ++ */ ++ int getUsbSafeMode(); ++ + private: + //QStringList m_source_uris; + QString m_dest_dir_uri = nullptr; +diff --git a/libpeony-qt/file-operation/file-node-reporter.h b/libpeony-qt/file-operation/file-node-reporter.h +index a727987..9e7202d 100644 +--- a/libpeony-qt/file-operation/file-node-reporter.h ++++ b/libpeony-qt/file-operation/file-node-reporter.h +@@ -64,7 +64,14 @@ public: + m_isUsedInCount = InCount; + } + } +- ++ qint64 getTotalCount() { ++ return m_totalCount; ++ } ++ void setTotalCount(const qint64 &totalCount) { ++ if (totalCount != m_totalCount) { ++ m_totalCount = totalCount; ++ } ++ } + Q_SIGNALS: + void nodeFound(const QString &uri, const qint64 &offset); + /*! +@@ -84,6 +91,7 @@ Q_SIGNALS: + private: + bool m_cancelled = false; + bool m_isUsedInCount = false; ++ qint64 m_totalCount = 0; + }; + + } +diff --git a/libpeony-qt/file-operation/file-node.cpp b/libpeony-qt/file-operation/file-node.cpp +index 2fa64dd..d0bff85 100644 +--- a/libpeony-qt/file-operation/file-node.cpp ++++ b/libpeony-qt/file-operation/file-node.cpp +@@ -26,20 +26,29 @@ + #include "file-node-reporter.h" + + #define PEONY_TRUNCATE_NAME_LIMIT 225 ++#define PEONY_ENCRYPTFS_TRUNCATE_LIMIT 125 + + using namespace Peony; + + FileNode::FileNode(QString uri, FileNode *parent, FileNodeReporter *reporter) + { +- char *basename = nullptr; ++// char *basename = nullptr; + m_uri = uri; + m_parent = parent; + m_reporter = reporter; + GFile *file = g_file_new_for_uri(uri.toUtf8().constData()); ++// basename = g_file_get_basename(file); ++// m_basename = basename; ++// m_dest_basename = basename; + //此处再次修正m_basename目的为解决编码问题,但截断方式后续仍需要优化 + m_basename = m_uri.split("/").last(); ++ //fix bug 247683复制百分号+数字或字母的文件/文件夹,粘贴成功后名称显示异常 ++ if (m_basename.contains("%")) { ++ QUrl qurl = QUrl(m_uri); ++ m_basename = qurl.fileName(QUrl::FullyEncoded); ++ } + m_dest_basename = m_basename; +- g_free(basename); ++// g_free(basename); + + //use G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS to avoid unnecessary recursion. + m_is_folder = g_file_query_file_type(file, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr) == G_FILE_TYPE_DIRECTORY; +@@ -97,6 +106,7 @@ void FileNode::findChildrenRecursively() + for (auto uri: uris) { + FileNode *node = new FileNode(uri, this, m_reporter); + m_children->append(node); ++ m_reporter->setTotalCount(m_reporter->getTotalCount() + 1); + node->findChildrenRecursively(); + } + } +@@ -144,7 +154,7 @@ const QString FileNode::resolveDestFileUri(const QString &destRootDir) + if (relativePath.endsWith("/")) { + relativePath.chop(1); + } +- QString url = FileUtils::urlEncode(destRootDir + "/" + relativePath); ++ QString url = FileUtils::urlEncode(destRootDir) + "/" + FileUtils::urlEncode(relativePath); + setDestUri(url); + return url; + } +@@ -155,8 +165,11 @@ void FileNode::truncateDestFileName(const int cateType) + auto destDirUri = FileUtils::getParentUri(destUri()); + auto fsType = FileUtils::getFsTypeFromFile(destDirUri); + bool setLimitBytes = true; ++ auto maxLimit = PEONY_TRUNCATE_NAME_LIMIT; + if (fsType.contains("ntfs")) { + setLimitBytes = false; ++ } else if (fsType.contains("ecryptfs")) { ++ maxLimit = PEONY_ENCRYPTFS_TRUNCATE_LIMIT; + } + auto suffix = m_basename; + suffix = suffix.remove(newName); +@@ -164,7 +177,7 @@ void FileNode::truncateDestFileName(const int cateType) + + if (setLimitBytes) { + bool useForceChop = false; +- if (suffix.toLocal8Bit().count() > PEONY_TRUNCATE_NAME_LIMIT) { ++ if (suffix.toLocal8Bit().count() > maxLimit) { + qWarning()<<"suffix too long:"<<m_uri<<"use force chop instead"; + useForceChop = true; + } else if (newName == m_basename) { +@@ -176,16 +189,16 @@ void FileNode::truncateDestFileName(const int cateType) + if (useForceChop) { + newName = m_basename; + if (TurnCateType::Post == cateType) { +- while (newName.toLocal8Bit().count() > PEONY_TRUNCATE_NAME_LIMIT) { ++ while (newName.toLocal8Bit().count() > maxLimit) { + newName.chop(1); + } + } else if (TurnCateType::Front == cateType) { +- while (newName.toLocal8Bit().count() > PEONY_TRUNCATE_NAME_LIMIT) { ++ while (newName.toLocal8Bit().count() > maxLimit) { + newName.remove(0,1); + } + } + } else { +- int limitBytes = PEONY_TRUNCATE_NAME_LIMIT - suffix.toLocal8Bit().count(); ++ int limitBytes = maxLimit - suffix.toLocal8Bit().count(); + if (TurnCateType::Post == cateType) { + while (newName.toLocal8Bit().count() > limitBytes) { + newName.chop(1); +@@ -200,7 +213,7 @@ void FileNode::truncateDestFileName(const int cateType) + setDestFileName(newName); + } else { + bool useForceChop = false; +- if (suffix.length() > PEONY_TRUNCATE_NAME_LIMIT) { ++ if (suffix.length() > maxLimit) { + qWarning()<<"suffix too long:"<<m_uri<<"use force chop instead"; + useForceChop = true; + } else if (newName == m_basename) { +@@ -211,11 +224,11 @@ void FileNode::truncateDestFileName(const int cateType) + } + if (useForceChop) { + newName = m_basename; +- while (newName.length() > PEONY_TRUNCATE_NAME_LIMIT) { ++ while (newName.length() > maxLimit) { + newName.chop(1); + } + } else { +- int limitBytes = PEONY_TRUNCATE_NAME_LIMIT - suffix.length(); ++ int limitBytes = maxLimit - suffix.length(); + while (newName.length() > limitBytes) { + newName.chop(1); + } +diff --git a/libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp b/libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp +index 3f74959..1406918 100644 +--- a/libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp ++++ b/libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp +@@ -44,11 +44,15 @@ + + #include "file-utils.h" + #include "rename-editor.h" ++#include "file-operation-helper.h" + + #include "global-settings.h" + + #define PEONY_TRUNCATE_NAME_LIMIT 225 ++#define PEONY_ENCRYPTFS_TRUNCATE_LIMIT 125 + #define PEONY_RENAME_LIMIT 255 ++#define PEONY_ENCRYPTFS_NAME_LIMIT 143 ++#define PEONY_UDF_NAME_LIMIT 254 + + KyFileDialogRename::KyFileDialogRename(QWidget *parent) : KyFileOperationDialog(parent), Peony::FileOperationErrorHandler() + { +@@ -76,9 +80,20 @@ KyFileDialogRename::KyFileDialogRename(QWidget *parent) : KyFileOperationDialog( + void KyFileDialogRename::handle(Peony::FileOperationError &error) + { + Peony::ExceptionResponse responseCode = Peony::ExceptionResponse::Cancel; +- QString newName = Peony::FileUtils::getUriBaseName(error.destDirUri); ++ QString destDirUri = error.destDirUri; ++ QString newName = Peony::FileUtils::getUriBaseName(destDirUri); + newName = Peony::FileUtils::urlDecode(newName); +- ++ QString filesafeRealPath = "file://" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/.box"; ++ if (destDirUri.startsWith("filesafe:///") || destDirUri.startsWith(filesafeRealPath)) { ++ m_nameLimit = PEONY_ENCRYPTFS_NAME_LIMIT; ++ m_truncateLimit = PEONY_ENCRYPTFS_TRUNCATE_LIMIT; ++ } else if (destDirUri.startsWith("file:///media/")) { ++ auto destParentDirUri = Peony::FileUtils::getParentUri(destDirUri); ++ auto fsType = Peony::FileUtils::getFsTypeFromFile(destParentDirUri); ++ if (fsType.contains("udf")) { ++ m_nameLimit = PEONY_UDF_NAME_LIMIT; ++ } ++ } + auto stack = new QStackedWidget(this); + stack->setContentsMargins(20, 0, 20, 20); + auto layout = new QHBoxLayout; +@@ -100,7 +115,7 @@ void KyFileDialogRename::handle(Peony::FileOperationError &error) + } + case Peony::FileOpCopy: + line1 = tr("Copying \"%1\"").arg(newName); { +- auto destDir = Peony::FileUtils::urlDecode(error.destDirUri); ++ auto destDir = Peony::FileUtils::urlDecode(destDirUri); + QString fileName = destDir.split("/").last(); + destDir.chop(fileName.count() + 1); + line2 = tr("To \"%1\"").arg(destDir); +@@ -109,7 +124,7 @@ void KyFileDialogRename::handle(Peony::FileOperationError &error) + } + case Peony::FileOpMove: { + line1 = tr("Moving \"%1\"").arg(newName); +- auto destDir = Peony::FileUtils::urlDecode(error.destDirUri); ++ auto destDir = Peony::FileUtils::urlDecode(destDirUri); + QString fileName = destDir.split("/").last(); + destDir.chop(fileName.count() + 1); + line2 = tr("To \"%1\"").arg(destDir); +@@ -205,7 +220,7 @@ void KyFileDialogRename::handle(Peony::FileOperationError &error) + // textEdit->setFixedHeight(height); + + auto num = new QLabel; +- QString totalNum = QString("<span style=\"color:red;\">%1</span>/%2%3").arg(newName.toLocal8Bit().count()).arg(PEONY_RENAME_LIMIT).arg(tr("Bytes")); ++ QString totalNum = QString("<span style=\"color:red;\">%1</span>/%2%3").arg(newName.toLocal8Bit().count()).arg(m_nameLimit).arg(tr("Bytes")); + num->setText(totalNum); + auto buttonBox2 = new QDialogButtonBox(this); + buttonBox2->setStandardButtons(QDialogButtonBox::NoButton); +@@ -248,14 +263,14 @@ void KyFileDialogRename::handle(Peony::FileOperationError &error) + + connect(textEdit, &QTextEdit::textChanged, this, [=](){ + QString currentNum ; +- if (textEdit->toPlainText().toLocal8Bit().count() > PEONY_RENAME_LIMIT) { ++ if (textEdit->toPlainText().toLocal8Bit().count() > m_nameLimit) { + ensure2->setEnabled(false); + currentNum = QString("<span style=\"color:red;\">%1</span>").arg(textEdit->toPlainText().toLocal8Bit().count()); + } else { + currentNum = QString("<span style=\"color:\">%1</span>").arg(textEdit->toPlainText().toLocal8Bit().count()); + ensure2->setEnabled(true); + } +- QString totalNum = QString("%1/%2%3").arg(currentNum).arg(PEONY_RENAME_LIMIT).arg(tr("Bytes")); ++ QString totalNum = QString("%1/%2%3").arg(currentNum).arg(m_nameLimit).arg(tr("Bytes")); + num->setText(totalNum); + }); + +@@ -307,7 +322,7 @@ void KyFileDialogRename::handle(Peony::FileOperationError &error) + + connect(truncationGroup, QOverload<QAbstractButton*>::of(&QButtonGroup::buttonClicked), this, [=](QAbstractButton* button) { + const int id = truncationGroup->id(button); +- const QString destName = truncateDestFileName(error.destDirUri, id); ++ const QString destName = truncateDestFileName(destDirUri, id); + truncatedLabel->setText(destName); + }); + Q_EMIT truncationGroup->buttonClicked(laterTruncation); +@@ -325,7 +340,7 @@ void KyFileDialogRename::handle(Peony::FileOperationError &error) + QString clickText = "<a href=\" \" style=\"color: #3D6BE5;text-decoration: none;\">" + + tr("truncate interval") + + "</a>" + tr("."); +- QString text = tr("Explanation: Truncate the portion of the file name that exceeds 225 bytes and select"); ++ QString text = tr("Explanation: Truncate the portion of the file name that exceeds %1 bytes and select").arg(m_truncateLimit); + text = text + clickText; + specificationLabel->setText(text); + m_currentWidget = truncationWidget; +@@ -343,7 +358,7 @@ void KyFileDialogRename::handle(Peony::FileOperationError &error) + QString clickText = "<a href=\" \" style=\"color: #3D6BE5;text-decoration: none;\">" + + tr("modify the name") + + "</a>" + tr("."); +- QString text = tr("Explanation: When renaming a file name, ensure it is within 255 bytes and "); ++ QString text = tr("Explanation: When renaming a file name, ensure it is within %1 bytes and ").arg(m_nameLimit); + text = text + clickText; + specificationLabel->setText(text); + m_currentWidget = page2; +@@ -459,7 +474,7 @@ const QString KyFileDialogRename::truncateDestFileName(const QString &uri, const + QString baseName = Peony::FileUtils::getUriBaseName(uri); + baseName = Peony::FileUtils::urlDecode(baseName); + auto destDirUri = Peony::FileUtils::getParentUri(uri); +- auto fsType = Peony::FileUtils::getFsTypeFromFile(uri); ++ auto fsType = Peony::FileUtils::getFsTypeFromFile(destDirUri); + bool setLimitBytes = true; + if (fsType.contains("ntfs")) { + setLimitBytes = false; +@@ -472,7 +487,7 @@ const QString KyFileDialogRename::truncateDestFileName(const QString &uri, const + QString destName; + if (setLimitBytes) { + bool useForceChop = false; +- if (suffix.toLocal8Bit().count() > PEONY_TRUNCATE_NAME_LIMIT) { ++ if (suffix.toLocal8Bit().count() > m_truncateLimit) { + qWarning()<<"suffix too long:"<<uri<<"use force chop instead"; + useForceChop = true; + } else if (newName == baseName) { +@@ -485,13 +500,13 @@ const QString KyFileDialogRename::truncateDestFileName(const QString &uri, const + newName = baseName; + int len = newName.length(); + if (Peony::TurnCateType::Post == cateType) { +- while (newName.toLocal8Bit().count() > PEONY_TRUNCATE_NAME_LIMIT) { ++ while (newName.toLocal8Bit().count() > m_truncateLimit) { + newName.chop(1); + } + truncatedText = baseName.right(len - newName.length()); + destName = newName + "<s>" + truncatedText + "</s>"; + } else if (Peony::TurnCateType::Front == cateType) { +- while (newName.toLocal8Bit().count() > PEONY_TRUNCATE_NAME_LIMIT) { ++ while (newName.toLocal8Bit().count() > m_truncateLimit) { + newName.remove(0,1); + } + truncatedText = baseName.left(len - newName.length()); +@@ -500,7 +515,7 @@ const QString KyFileDialogRename::truncateDestFileName(const QString &uri, const + } else { + QString tmp = newName; + int len = tmp.length(); +- int limitBytes = PEONY_TRUNCATE_NAME_LIMIT - suffix.toLocal8Bit().count(); ++ int limitBytes = m_truncateLimit - suffix.toLocal8Bit().count(); + if (Peony::TurnCateType::Post == cateType) { + while (newName.toLocal8Bit().count() > limitBytes) { + newName.chop(1); +@@ -518,7 +533,7 @@ const QString KyFileDialogRename::truncateDestFileName(const QString &uri, const + } + } else { + bool useForceChop = false; +- if (suffix.length() > PEONY_TRUNCATE_NAME_LIMIT) { ++ if (suffix.length() > m_truncateLimit) { + qWarning()<<"suffix too long:"<<uri<<"use force chop instead"; + useForceChop = true; + } else if (newName == baseName) { +@@ -529,7 +544,7 @@ const QString KyFileDialogRename::truncateDestFileName(const QString &uri, const + } + if (useForceChop) { + newName = baseName; +- while (newName.length() > PEONY_TRUNCATE_NAME_LIMIT) { ++ while (newName.length() > m_truncateLimit) { + newName.chop(1); + } + int len = baseName.length(); +@@ -537,7 +552,7 @@ const QString KyFileDialogRename::truncateDestFileName(const QString &uri, const + destName = newName + "<s>" + truncatedText + "</s>"; + } else { + QString tmp = newName; +- int limitBytes = PEONY_TRUNCATE_NAME_LIMIT - suffix.length(); ++ int limitBytes = m_truncateLimit - suffix.length(); + while (newName.length() > limitBytes) { + newName.chop(1); + } +diff --git a/libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.h b/libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.h +index 779823d..bef6f95 100644 +--- a/libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.h ++++ b/libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.h +@@ -48,6 +48,9 @@ public: + const QString truncateDestFileName(const QString &uri, const int cateType); + QWidget *m_currentWidget = nullptr; + ++ int m_nameLimit = 255; ++ int m_truncateLimit = 225; ++ + private: + QDBusInterface *m_statusManagerDBus = nullptr; + }; +diff --git a/libpeony-qt/file-operation/file-operation-error-dialog-base.cpp b/libpeony-qt/file-operation/file-operation-error-dialog-base.cpp +index f5cdc9a..36b8926 100644 +--- a/libpeony-qt/file-operation/file-operation-error-dialog-base.cpp ++++ b/libpeony-qt/file-operation/file-operation-error-dialog-base.cpp +@@ -171,18 +171,32 @@ void Peony::FileOperationErrorDialogBase::adjustTextContent() + text.remove(text.length() - 4, 4); + text.remove(0, 3); + } ++ ++ // 获取文本的边界矩形 + auto rect = fontMetrics().boundingRect(text); + bool oneline = rect.width() < m_tipcontent->width(); +- int topMargin = 0; +- if (!oneline) { +- topMargin = qMax(32 - fontMetrics().height(), 0); +- } else { +- topMargin = qMax(48 - fontMetrics().height(), 0); +- } ++ ++ // 计算缩放系数(考虑高 DPI 缩放) ++ qreal scaleFactor = devicePixelRatioF(); ++ int adjustedHeight = qRound(fontMetrics().height() * scaleFactor); ++ ++ // 设置文本的上边距 ++ int topMargin = oneline ? qMax(48 - adjustedHeight, 0) : qMax(32 - adjustedHeight, 0); + m_tipcontent->setContentsMargins(0, topMargin, 0, 0); +- int pimageMargin = 0; +- pimageMargin = qMax(topMargin - (m_tipimage->pixmap()->height()-fontMetrics().height())/2, 0); ++ ++ // 获取图标的高度并考虑缩放 ++ int imageHeight = qRound(m_tipimage->pixmap()->height() / scaleFactor); ++ int pimageMargin = qMax(topMargin - (imageHeight - adjustedHeight) / 2, 0); + m_tipimage->setContentsMargins(0, pimageMargin, 0, 0); ++ ++ // 额外居中调整:根据容器高度来居中 ++ int totalHeight = rect.height() + imageHeight; ++ int containerHeight = m_tipcontent->height(); ++ if (totalHeight < containerHeight) { ++ int verticalAlignOffset = (containerHeight - totalHeight) / 2; ++ m_tipcontent->setContentsMargins(0, topMargin + verticalAlignOffset, 0, 0); ++ m_tipimage->setContentsMargins(0, pimageMargin + verticalAlignOffset, 0, 0); ++ } + } + + void Peony::FileOperationErrorDialogBase::setText(QString text) +diff --git a/libpeony-qt/file-operation/file-operation-error-dialogs.cpp b/libpeony-qt/file-operation/file-operation-error-dialogs.cpp +index 5b715e1..a95c911 100644 +--- a/libpeony-qt/file-operation/file-operation-error-dialogs.cpp ++++ b/libpeony-qt/file-operation/file-operation-error-dialogs.cpp +@@ -130,9 +130,16 @@ void Peony::FileOperationErrorDialogConflict::handle (FileOperationError& error) + setTipFileicon(file.getInfo()->iconName()); + setTipFilename(file.getInfo()->displayName()); + } else { +- QString fileName = error.srcUri.split("/").back(); ++ QString url; ++ QString fileName = FileUtils::urlDecode(error.srcUri).split("/").back(); + //fix bug 148806, matches end path name +- QString url = error.destDirUri.split("/").back().contains(fileName) ? error.destDirUri : error.destDirUri + "/" + fileName; ++ if (error.destDirUri.split("/").back().contains(fileName) ++ || FileUtils::urlDecode(error.destDirUri).split("/").back().contains(fileName) ++ || error.srcUri.startsWith("trash:///")) { ++ url = error.destDirUri; ++ } else { ++ url = error.destDirUri + "/" + fileName; ++ } + FileInfoJob file(url, nullptr); + file.querySync(); + setTipFileicon(file.getInfo()->iconName()); +@@ -217,7 +224,17 @@ Peony::FileOperationErrorDialogWarning::FileOperationErrorDialogWarning(Peony::F + m_cancel = true; + done(QDialog::Rejected); + }); +- ++ QCheckBox* c = addCheckBoxLeft (tr("Skip all")); ++ connect(c, &QCheckBox::stateChanged, this, [=](int chose) { ++ switch (chose) { ++ case Qt::Checked: ++ m_do_same = true; ++ break; ++ case Qt::Unchecked: ++ default: ++ m_do_same = false; ++ } ++ }); + } + + Peony::FileOperationErrorDialogWarning::~FileOperationErrorDialogWarning() +@@ -255,14 +272,28 @@ void Peony::FileOperationErrorDialogWarning::handle(Peony::FileOperationError &e + + int ret = exec(); + ++ if (QDialog::Rejected == ret || m_error->errorCode == G_IO_ERROR_CANCELLED) { ++ error.respCode = Cancel; ++ return; ++ } ++ ++ // Delete file to the Recycle Bin error, prompt whether to force deletion ++ if (m_error->op == FileOpTrash && m_error->errorCode == G_IO_ERROR_FILENAME_TOO_LONG) { ++ error.respCode = Force; ++ return; ++ } ++ ++ if (m_do_same) { ++ error.respCode = IgnoreAll; ++ return; ++ } ++ + switch (m_error->errorCode) { + case G_IO_ERROR_BUSY: + case G_IO_ERROR_PENDING: + case G_IO_ERROR_NO_SPACE: +- case G_IO_ERROR_CANCELLED: + case G_IO_ERROR_INVALID_DATA: + case G_IO_ERROR_NOT_SUPPORTED: +- case G_IO_ERROR_PERMISSION_DENIED: + case G_IO_ERROR_CANT_CREATE_BACKUP: + case G_IO_ERROR_TOO_MANY_OPEN_FILES: + error.respCode = Cancel; +@@ -271,18 +302,13 @@ void Peony::FileOperationErrorDialogWarning::handle(Peony::FileOperationError &e + error.respCode = IgnoreAll; + break; + default: +- error.respCode = IgnoreOne; ++ if (m_do_same) { ++ error.respCode = IgnoreAll; ++ } else { ++ error.respCode = IgnoreOne; ++ } + break; + } +- +- // Delete file to the Recycle Bin error, prompt whether to force deletion +- if (QDialog::Accepted == ret && m_error->op == FileOpTrash && m_error->errorCode == G_IO_ERROR_FILENAME_TOO_LONG) { +- error.respCode = Force; +- } +- +- if (QDialog::Rejected == ret) { +- error.respCode = Cancel; +- } + } + + static QPixmap drawSymbolicColoredPixmap (const QPixmap& source) +@@ -309,7 +335,7 @@ Peony::FileOperationErrorDialogNotSupported::FileOperationErrorDialogNotSupporte + { + setIcon ("dialog-warning"); + +- QPushButton* b = addButton (tr("No")); ++ QPushButton* b = addButton (tr("Cancel")); + b->setBackgroundRole(QPalette::Button); + connect(b, &QPushButton::pressed, this, [=] () { + m_ok = false; +@@ -317,7 +343,7 @@ Peony::FileOperationErrorDialogNotSupported::FileOperationErrorDialogNotSupporte + done(QDialog::Rejected); + }); + +- b = addButton (tr("Yes")); ++ b = addButton (tr("Delete")); + b->setBackgroundRole(QPalette::Button); + connect(b, &QPushButton::pressed, this, [=] () { + m_ok = true; +diff --git a/libpeony-qt/file-operation/file-operation-error-dialogs.h b/libpeony-qt/file-operation/file-operation-error-dialogs.h +index 0968bb2..2695553 100644 +--- a/libpeony-qt/file-operation/file-operation-error-dialogs.h ++++ b/libpeony-qt/file-operation/file-operation-error-dialogs.h +@@ -63,6 +63,7 @@ public: + private: + bool m_ok = false; + bool m_cancel = false; ++ bool m_do_same = false; + + QPushButton* m_cancel_btn = nullptr; + }; +diff --git a/libpeony-qt/file-operation/file-operation-helper.cpp b/libpeony-qt/file-operation/file-operation-helper.cpp +index 1377734..f112b41 100644 +--- a/libpeony-qt/file-operation/file-operation-helper.cpp ++++ b/libpeony-qt/file-operation/file-operation-helper.cpp +@@ -1,4 +1,4 @@ +-/* ++ /* + * Peony-Qt's Library + * + * Copyright (C) 2023, KylinSoft Co., Ltd. +@@ -50,6 +50,11 @@ QString FileOperationHelper::getDiscType() + return m_disc_media_type; + } + ++QString FileOperationHelper::getDiscSystemType() ++{ ++ return m_disc_system_type; ++} ++ + bool FileOperationHelper::isUnixCDDevice() + { + if (!m_unix_device.isEmpty() && m_unix_device.startsWith("/dev/sr")) { +@@ -156,12 +161,9 @@ bool FileOperationHelper::discWriteOperation(const QStringList &sourUrisList, co + } else { + m_disc_error_msg = tr("Burn failed"); + } +- qDebug() << "udf clint error message: " << errinfo; ++ qDebug() << "udf clint start error message: " << errinfo; + for (QString filePath : list) { +- QFile file(filePath); +- if (file.exists()) { +- file.remove(); +- } ++ deleteDirectoryRecursively(filePath); + } + udfwrite->closeUdfClient(); + free(errinfo); +@@ -175,10 +177,7 @@ bool FileOperationHelper::discWriteOperation(const QStringList &sourUrisList, co + m_disc_error_msg = tr("Burn failed"); + qDebug() << "udf write error message: " << errinfo; + for (QString filePath : list) { +- QFile file(filePath); +- if (file.exists()) { +- file.remove(); +- } ++ deleteDirectoryRecursively(filePath); + } + udfwrite->closeUdfClient(); + free(errinfo); +@@ -281,6 +280,35 @@ QString FileOperationHelper::getDestName(const QString &destUri) + return destName; + } + ++void FileOperationHelper::deleteDirectoryRecursively(const QString &dirPath) { ++ QDir dir(dirPath); ++ ++ if (!dir.exists()) { ++ QFile file(dirPath); ++ if (file.exists()) { ++ file.remove(); ++ } ++ qDebug() << "Directory does not exist:" << dirPath; ++ return; ++ } ++ ++ QFileInfoList filesAndDirs = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Files); ++ for (const QFileInfo &info : filesAndDirs) { ++ if (info.isDir()) { ++ deleteDirectoryRecursively(info.absoluteFilePath()); ++ } else { ++ QFile file(info.absoluteFilePath()); ++ if (!file.remove()) { ++ qDebug() << "Failed to delete file:" << info.absoluteFilePath(); ++ } ++ } ++ } ++ ++ if (!dir.rmdir(dirPath)) { ++ qDebug() << "Failed to remove directory:" << dirPath; ++ } ++} ++ + QString FileOperationHelper::getDiscError() + { + return m_disc_error_msg; +diff --git a/libpeony-qt/file-operation/file-operation-helper.h b/libpeony-qt/file-operation/file-operation-helper.h +index 45e4049..df8c8c1 100644 +--- a/libpeony-qt/file-operation/file-operation-helper.h ++++ b/libpeony-qt/file-operation/file-operation-helper.h +@@ -51,6 +51,8 @@ public: + + QString getDestName(const QString &destUri); + ++ QString getDiscSystemType(); ++ + QString getDiscError(); + protected: + /** +@@ -61,6 +63,8 @@ protected: + */ + QString matchingUnixDevice(QString uri); + ++ void deleteDirectoryRecursively(const QString &dirPath); ++ + private: + bool m_is_disk_work = false; + QString m_unix_device = nullptr; +diff --git a/libpeony-qt/file-operation/file-operation-manager.cpp b/libpeony-qt/file-operation/file-operation-manager.cpp +index 8593cd1..0640702 100644 +--- a/libpeony-qt/file-operation/file-operation-manager.cpp ++++ b/libpeony-qt/file-operation/file-operation-manager.cpp +@@ -63,6 +63,9 @@ + #include <QDebug> + #include <unistd.h> + ++#include <QAction> ++#include <QWindow> ++ + using namespace Peony; + #ifdef KY_SDK_SOUND_EFFECTS + using namespace kdk; +@@ -279,6 +282,16 @@ void FileOperationManager::startOperation(FileOperation *operation, bool addToHi + QWidget *widget = QApplication::topLevelAt(QCursor::pos()); + qDebug()<<"top level widget:"<<widget<<",current QPoint:"<<QCursor::pos(); + FileOperationInternalDialog questionbox((QDialog*)widget); ++ ++ auto windowProperty = property("rootWindow"); ++ if (windowProperty.isValid()) { ++ auto window = windowProperty.value<QWindow *>(); ++ if (window) { ++ questionbox.createWinId(); ++ questionbox.windowHandle()->setTransientParent(window); ++ } ++ } ++ + auto okButton = questionbox.addButton(tr("OK")); + connect(okButton, &QPushButton::clicked, &questionbox, [&]{ + questionbox.accept(); +@@ -288,6 +301,9 @@ void FileOperationManager::startOperation(FileOperation *operation, bool addToHi + questionbox.reject(); + }); + okButton->setFocus(); ++ okButton->setProperty("isImportant", true); ++ cancelButton->setProperty("useButtonPalette", true); ++ + questionbox.setText(tr("Do you want to put selected %1 item(s) into trash?").arg(operationInfo.get()->sources().count())); + questionbox.setIcon("user-trash"); + auto checkbox = questionbox.addCheckBoxLeft(tr("Do not show again")); +@@ -458,7 +474,7 @@ start: + QString name; + + if (mountRootName == "/") { +- name = tr("File System"); ++ name = tr("System Disk"); + } else if (mountRootName == "/data") { + name = tr("Data"); + } else { +@@ -553,7 +569,13 @@ start: + } + operation->setHasError(true); + }); +- ++#ifdef KY_UDF_BURN ++ operation->connect(operation, &FileOperation::operationUdfBurnRunning, this, [=](bool udfBurnRunning){ ++ if (m_isUdfBurnRunning != udfBurnRunning) { ++ m_isUdfBurnRunning = udfBurnRunning; ++ } ++ }); ++#endif + operation->connect(operation, &FileOperation::errored, this, &FileOperationManager::handleError, Qt::BlockingQueuedConnection); + operation->connect(operation, &FileOperation::operationFinished, this, [=](){ + //story 19796,后续数据处理 +@@ -614,6 +636,9 @@ start: + this->clearHistory(); + } + } ++ if (isUdfBurnRunning()) { ++ m_isUdfBurnRunning = false; ++ } + }, Qt::BlockingQueuedConnection); + + // fix #92481 +@@ -738,6 +763,11 @@ bool FileOperationManager::canRedo() + return !m_redo_stack.isEmpty(); + } + ++bool FileOperationManager::isUdfBurnRunning() ++{ ++ return m_isUdfBurnRunning; ++} ++ + std::shared_ptr<FileOperationInfo> FileOperationManager::getUndoInfo() + { + return m_undo_stack.top(); +@@ -983,6 +1013,31 @@ void FileOperationManager::slot_moveFilesToAnotherProcCompleted(const QStringLis + + } + ++QList<QAction *> FileOperationManager::getUndoRedoActions() ++{ ++ QList<QAction *> l; ++ if (!qApp) ++ return l; ++ if (canUndo()) { ++ auto opInfo = m_undo_stack.top(); ++ QString opName = opInfo->getOperationName(); ++ auto action = new QAction(tr("Undo %1").arg(opName)); ++ action->setShortcut(QKeySequence::Undo); ++ connect(action, &QAction::triggered, this, &FileOperationManager::undo); ++ l<<action; ++ } ++ if (canRedo()) { ++ auto opInfo = m_redo_stack.top(); ++ QString opName = opInfo->getOperationName(); ++ auto action = new QAction(tr("Redo %1").arg(opName)); ++ action->setShortcut(QKeySequence::Redo); ++ connect(action, &QAction::triggered, this, &FileOperationManager::redo); ++ l<<action; ++ } ++ return l; ++} ++ ++ + //FIXME: get opposite info correcty. + FileOperationInfo::FileOperationInfo(QStringList srcUris, + QString destDirUri, +@@ -1157,6 +1212,44 @@ void FileOperationInfo::setOperationRecording(bool state) + } + } + ++QString FileOperationInfo::getOperationName() ++{ ++ QString opName; ++ switch (m_type) { ++ case FileOperationInfo::Copy: ++ opName = tr("Copy"); ++ break; ++ case FileOperationInfo::Move: ++ opName = tr("Move"); ++ break; ++ case FileOperationInfo::Rename: ++ case FileOperationInfo::BatchRename: ++ case FileOperationInfo::BatchRenameInternal: ++ opName = tr("Rename"); ++ break; ++ case FileOperationInfo::Link: ++ opName = tr("Link"); ++ break; ++ case FileOperationInfo::Trash: ++ opName = tr("Delete"); ++ break; ++ case FileOperationInfo::Delete: ++ opName = tr("Delete Permanently"); ++ break; ++ case FileOperationInfo::Untrash: ++ opName = tr("Restore"); ++ break; ++ case FileOperationInfo::CreateFolder: ++ case FileOperationInfo::CreateTemplate: ++ case FileOperationInfo::CreateTxt: ++ opName = tr("New"); ++ break; ++ default: ++ break; ++ } ++ return opName; ++} ++ + // S3/S4 + void FileOperationManager::systemSleep (GDBusConnection *connection, const gchar *senderName, const gchar *objectPath, const gchar *interfaceName, const gchar *signalName, GVariant *parameters, gpointer udata) + { +diff --git a/libpeony-qt/file-operation/file-operation-manager.h b/libpeony-qt/file-operation/file-operation-manager.h +index c59f88d..a45a344 100644 +--- a/libpeony-qt/file-operation/file-operation-manager.h ++++ b/libpeony-qt/file-operation/file-operation-manager.h +@@ -76,7 +76,7 @@ public: + bool isAllowParallel(); + void setFsyncStatus(bool synchronizing = true); + bool isFsynchronizing(); +- ++ bool isUdfBurnRunning(); + + QStringList getFilesOpenedByProc(const QString &procName); + +@@ -144,6 +144,8 @@ public Q_SLOTS: + void slot_opreateFinishedOfEngrampa(const QString& path, bool finish);/* hotfix bug#188622 【文件管理器】连接共享文件夹后进行压缩/解压缩操作,需要手动刷新后才会显示 */ + void slot_moveFilesToAnotherProcCompleted(const QStringList& srcUris);/*跨进程move操作完成后更新视图,目前用于从搜索页面剪切或拖拽到桌面,linkto story#23915 */ + ++ QList<QAction *> getUndoRedoActions(); ++ + private: + explicit FileOperationManager(QObject *parent = nullptr); + ~FileOperationManager(); +@@ -173,6 +175,7 @@ private: + QDBusInterface* m_iface = nullptr; + bool m_isFsynchronizing = false; + QMutex m_fsyncMutex; ++ bool m_isUdfBurnRunning = false; + + QThread *m_replicaThread = nullptr; + RemoteFileEventHelper *m_replica = nullptr; +@@ -232,6 +235,8 @@ public: + return m_dest_dir_uri; + } + ++ QString getOperationName(); ++ + //private: + //normal operation src uris and dest uris + QStringList m_src_uris; +diff --git a/libpeony-qt/file-operation/file-operation-progress-bar-helper.cpp b/libpeony-qt/file-operation/file-operation-progress-bar-helper.cpp +new file mode 100644 +index 0000000..e6d6487 +--- /dev/null ++++ b/libpeony-qt/file-operation/file-operation-progress-bar-helper.cpp +@@ -0,0 +1,56 @@ ++#include "file-operation-progress-bar-helper.h" ++#include <QDebug> ++#define MEGABYTE 1048576.0 ++ ++progressBarHelper::progressBarHelper(QObject *parent) : QObject(parent) ++{ ++ ++} ++ ++double progressBarHelper::calculateSpeed(const qint64 ¤tSize, const double &elapsedSeconds) ++{ ++ double currentSpeed = 0.0; ++ currentSpeed = currentSize / MEGABYTE / elapsedSeconds; ++ qDebug() << currentSpeed << "Mb/s"; ++ return currentSpeed; ++} ++ ++int progressBarHelper::calculateEstimatedTime(const qint64 &residualSize ,const double ¤tSpeed) ++{ ++ int estimatedTime = 0; ++ estimatedTime = residualSize / MEGABYTE / currentSpeed; ++ qDebug()<< "estimated time" << estimatedTime; ++ return estimatedTime; ++} ++ ++QString progressBarHelper::timeToString(const int &time) ++{ ++ int days = time / 86400; ++ int hours = (time % 86400) / 3600; ++ int minutes = (time % 3600) / 60; ++ int remainingSeconds = time % 60; ++ QString currentEstimatedTime = tr("Calculating time"); ++ if(days > 31) { ++ //计算时长超过一个月认为数据有误,不进行展示 ++ return currentEstimatedTime; ++ } ++ if (days > 0) { ++ currentEstimatedTime = QString(tr("%1day%2hrs%3mins%4sec")) ++ .arg(days) ++ .arg(hours, 2, 10, QLatin1Char('0')) ++ .arg(minutes, 2, 10, QLatin1Char('0')) ++ .arg(remainingSeconds, 2, 10, QLatin1Char('0')); ++ } else if (hours > 0) { ++ currentEstimatedTime = QString(tr("%1hrs%2mins%3sec")) ++ .arg(hours, 2, 10, QLatin1Char('0')) ++ .arg(minutes, 2, 10, QLatin1Char('0')) ++ .arg(remainingSeconds, 2, 10, QLatin1Char('0')); ++ } else if (minutes > 0) { ++ currentEstimatedTime = QString(tr("%1 mins%2sec")) ++ .arg(minutes) ++ .arg(remainingSeconds, 2, 10, QLatin1Char('0')); ++ } else if (remainingSeconds > 0){ ++ currentEstimatedTime = QString(tr("%1sec")).arg(remainingSeconds); ++ } ++ return currentEstimatedTime; ++} +diff --git a/libpeony-qt/file-operation/file-operation-progress-bar-helper.h b/libpeony-qt/file-operation/file-operation-progress-bar-helper.h +new file mode 100644 +index 0000000..1472b62 +--- /dev/null ++++ b/libpeony-qt/file-operation/file-operation-progress-bar-helper.h +@@ -0,0 +1,15 @@ ++#ifndef PROGRESSBARHELPER_H ++#define PROGRESSBARHELPER_H ++#include <qobject.h> ++ ++class progressBarHelper : public QObject ++{ ++ Q_OBJECT ++public: ++ explicit progressBarHelper(QObject *parent = nullptr); ++ static double calculateSpeed(const qint64 ¤tSize, const double &elapsedSeconds); ++ static int calculateEstimatedTime(const qint64 &residualSize ,const double ¤tSpeed); ++ static QString timeToString(const int &time); ++}; ++ ++#endif // PROGRESSBARHELPER_H +diff --git a/libpeony-qt/file-operation/file-operation-progress-bar.cpp b/libpeony-qt/file-operation/file-operation-progress-bar.cpp +index e347adc..c280a66 100644 +--- a/libpeony-qt/file-operation/file-operation-progress-bar.cpp ++++ b/libpeony-qt/file-operation/file-operation-progress-bar.cpp +@@ -29,6 +29,7 @@ + #include <QPushButton> + #include <QMessageBox> + #include <QToolTip> ++#include <QTime> + #include <QTimer> + #include "file-utils.h" + #include "xatom-helper.h" +@@ -41,6 +42,7 @@ + #include <QX11Info> + #include "xatom-helper.h" + #endif ++#define MEGABYTE 1048576.0 + + QPushButton* btn; + +@@ -172,7 +174,7 @@ void FileOperationProgressBar::removeFileOperation(ProgressBar *progress) + + bool FileOperationProgressBar::isInhibit() + { +- return m_fds != nullptr; ++ return m_fds1 != nullptr; + } + + FileOperationProgressBar::FileOperationProgressBar(QWidget *parent) : QWidget(parent) +@@ -296,6 +298,28 @@ void FileOperationProgressBar::showMore() + update(); + } + ++void FileOperationProgressBar::closeEvent(QCloseEvent *event) ++{ ++ if (event) { ++ for (auto pg = m_widget_list->constBegin(); pg != m_widget_list->constEnd(); ++pg) { ++ Q_EMIT pg.value()->cancelled(); ++ } ++ Q_EMIT canceled(); ++ } ++} ++ ++bool FileOperationProgressBar::event(QEvent *event) ++{ ++ if (event->type() == QEvent::Show) { ++ MotifWmHints hints; ++ hints.flags = MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS; ++ hints.functions = MWM_FUNC_ALL; ++ hints.decorations = MWM_DECOR_BORDER; ++ XAtomHelper::getInstance()->setWindowMotifHint(winId(), hints); ++ } ++ return QWidget::event(event); ++} ++ + bool FileOperationProgressBar::inhibit() + { + g_autoptr(GError) error = NULL; +@@ -313,11 +337,20 @@ bool FileOperationProgressBar::inhibit() + g_autoptr(GVariant) ret = g_dbus_connection_call_with_unix_fd_list_sync(pconnection, "org.freedesktop.login1", "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", "Inhibit", + g_variant_new("(ssss)", "sleep", "peony", "file operation", "block"), +- rtype, G_DBUS_CALL_FLAGS_NONE, G_MAXINT, NULL, &m_fds, NULL, &error); ++ rtype, G_DBUS_CALL_FLAGS_NONE, G_MAXINT, NULL, &m_fds1, NULL, &error); + if (error) { + printf("cannot block s4: %s\n", error->message); + } + ++ ++ ret = g_dbus_connection_call_with_unix_fd_list_sync(pconnection, "org.freedesktop.login1", "/org/freedesktop/login1", ++ "org.freedesktop.login1.Manager", "Inhibit", ++ g_variant_new("(ssss)", "shutdown", "peony", "file operation", "block"), ++ rtype, G_DBUS_CALL_FLAGS_NONE, G_MAXINT, NULL, &m_fds2, NULL, &error); ++ if (error) { ++ printf("cannot block s5: %s\n", error->message); ++ } ++ + Q_UNUSED(ret); + } + +@@ -326,9 +359,13 @@ bool FileOperationProgressBar::inhibit() + + void FileOperationProgressBar::uninhibit() + { +- if (m_fds) { +- g_object_unref(m_fds); +- m_fds = nullptr; ++ if (m_fds1) { ++ g_object_unref(m_fds1); ++ m_fds1 = nullptr; ++ } ++ if (m_fds2) { ++ g_object_unref(m_fds2); ++ m_fds2 = nullptr; + } + } + +@@ -435,7 +472,7 @@ MainProgressBar::MainProgressBar(QWidget *parent) : QWidget(parent) + setWindowFlags(Qt::FramelessWindowHint); + setMouseTracking(true); + +- m_title = tr("File operation"); ++ m_title = tr("File Operation"); + + m_btn_pause = new QToolButton (this); + m_btn_close = new QPushButton(this); +@@ -491,6 +528,7 @@ void MainProgressBar::initPrarm() + m_stopping = false; + m_current_value = 0.0; + m_file_name = tr("starting ..."); ++ m_current_estimated_time = tr("Calculating time"); + } + + void MainProgressBar::setFileIcon(QIcon& icon) +@@ -536,6 +574,7 @@ QString MainProgressBar::elideText(const QFont &font, const int &width, const QS + QString display_name = strInfo; + if(fontMetrics.width(strInfo) > 2*width - 20) { + display_name = QFontMetrics(font).elidedText(strInfo, Qt::ElideMiddle, 2*width-20); ++ this->setToolTip(m_file_name); + } + return display_name; + +@@ -719,7 +758,7 @@ void MainProgressBar::paintContent(QPainter &painter) + painter.drawText(m_file_name_x, m_file_name_y, m_file_name_w, m_file_name_height, Qt::AlignLeft | Qt::AlignVCenter, tr("sync ...")); + painter.drawPixmap(m_progress_pause_x, m_progress_pause_y, drawSymbolicColoredPixmap(QIcon::fromTheme("media-playback-pause-symbolic").pixmap(m_pause_btn_height, m_pause_btn_height))); + } else { +- this->setToolTip(m_file_name); ++// this->setToolTip(m_file_name); + QString display_name; + display_name = elideText(this->font(), m_file_name_w, m_file_name); + //修改藏文下显示不全的问题 +@@ -746,6 +785,9 @@ void MainProgressBar::paintContent(QPainter &painter) + painter.drawText(m_percent_x, m_percent_y, m_fix_width - m_percent_margin, m_percent_height, Qt::AlignRight | Qt::AlignBottom, + QString(" %1 %").arg(QString::number(m_current_value * 100, 'f', 1))); + ++ painter.drawText(m_percent_x, m_percent_y, m_fix_width - m_percent_margin, m_percent_height, Qt::AlignLeft | Qt::AlignBottom, ++ QString(tr(" %1Mb/s Est. time left: %2")).arg(QString::number(m_current_speed, 'f', 1)).arg(m_current_estimated_time)); ++ + painter.restore(); + } + +@@ -771,10 +813,14 @@ void MainProgressBar::cancelld() + update(); + } + +-void MainProgressBar::updateValue(QString& name, QIcon& icon, double value) ++void MainProgressBar::updateValue(QString& name, QIcon& icon, double value, double speed, int time) + { + if (value >= 0 && value < 1) { + m_current_value = value; ++ m_current_speed = speed; ++ if (speed > 0.0 && time > 0) { ++ m_current_estimated_time = progressBarHelper::timeToString(time); ++ } + } + + m_file_name = Peony::FileUtils::urlDecode(name); +@@ -822,7 +868,7 @@ void OtherButton::paintEvent(QPaintEvent *event) + painter.setFont(font); + pen.setBrush(QBrush(btn->palette().color(QPalette::WindowText))); + painter.setPen(pen); +- painter.drawText(textArea, Qt::AlignLeft | Qt::AlignVCenter, tr("Other queue")); ++ painter.drawText(textArea, Qt::AlignLeft | Qt::AlignVCenter, tr("Other Queue")); + + painter.restore(); + +@@ -858,6 +904,9 @@ ProgressBar::ProgressBar(QWidget *parent) : QWidget(parent) + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + m_update_count = 0; + m_dest_uri = tr("starting ..."); ++// QTimer *timer = new QTimer(this); ++// connect(timer, &QTimer::timeout, this, &ProgressBar::calculateSpeed); ++// timer->start(1000); + connect(this, &ProgressBar::cancelled, this, &ProgressBar::onCancelled); + connect(this, &ProgressBar::destroyed, this, [=] () {m_has_finished = true;}); + } +@@ -1089,7 +1138,7 @@ void ProgressBar::updateValue(double value) + m_current_value = value; + } + +- Q_EMIT sendValue(m_dest_uri, getIcon(), m_current_value); ++ Q_EMIT sendValue(m_dest_uri, getIcon(), m_current_value, m_current_speed, m_estimated_time); + update(); + } + +@@ -1139,14 +1188,25 @@ void ProgressBar::updateProgress(const QString &srcUri, const QString &destUri, + } + + double currentPercent = current * 1.0 / total; ++ m_current_size = current; + //fix bug#133624,133380, delete all empty files, not update progress bar + // if (m_total_size <= 0 || 16 * m_total_count <= m_total_size){ + // m_update_count++; + // currentPercent = m_update_count * 1.0 /m_total_count; + // } +- ++ if (m_last_size != m_current_size) { ++ qint64 elapsedMilliseconds = QDateTime::currentMSecsSinceEpoch() - m_start_time; ++ double elapsedSeconds = elapsedMilliseconds / 1000.0; ++ if (elapsedSeconds >= 1.0) { ++ auto size = m_current_size - m_last_size; ++ m_current_speed = progressBarHelper::calculateSpeed(size, elapsedSeconds); ++ auto residualSize = m_total_size - m_current_size; ++ m_estimated_time = progressBarHelper::calculateEstimatedTime(residualSize,m_current_speed); ++ m_last_size = m_current_size; ++ m_start_time = QDateTime::currentMSecsSinceEpoch(); ++ } ++ } + qDebug() << "progress bar: " << currentPercent <<current<<total<<m_update_count<<m_total_count; +- + updateValue(currentPercent); + + Q_UNUSED(srcUri); +diff --git a/libpeony-qt/file-operation/file-operation-progress-bar.h b/libpeony-qt/file-operation/file-operation-progress-bar.h +index 1f5c4e0..03d441b 100644 +--- a/libpeony-qt/file-operation/file-operation-progress-bar.h ++++ b/libpeony-qt/file-operation/file-operation-progress-bar.h +@@ -22,6 +22,8 @@ + + #ifndef FILEOPERATIONPROGRESS_H + #define FILEOPERATIONPROGRESS_H ++#include <gio/gio.h> ++ + #include <QWidget> + #include <QHBoxLayout> + #include <QListWidget> +@@ -29,6 +31,7 @@ + #include <QToolButton> + #include <gio/gio.h> + ++#include "file-operation-progress-bar-helper.h" + class ProgressBar; + class OtherButton; + class MainProgressBar; +@@ -56,6 +59,8 @@ private: + + protected: + void showWidgetList(bool show); ++ void closeEvent(QCloseEvent *event); ++ bool event(QEvent *event); + #if 0 + void mouseMoveEvent(QMouseEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; +@@ -75,8 +80,8 @@ public: + bool m_error = false; + + private: +- GUnixFDList* m_fds = nullptr; +- ++ GUnixFDList* m_fds1 = nullptr; ++ GUnixFDList* m_fds2 = nullptr; + // layout + QVBoxLayout* m_main_layout = nullptr; + GDBusConnection* m_dbus_connection = nullptr; +@@ -130,7 +135,7 @@ Q_SIGNALS: + void resume(); + void cancelled(); + void finished(ProgressBar* fop); +- void sendValue(QString&, QIcon&, double); ++ void sendValue(QString&, QIcon&, double, double, int); + + protected: + void paintEvent(QPaintEvent *event) override; +@@ -194,6 +199,8 @@ private: + // value + QIcon m_icon; + double m_current_value = 0.0; ++ double m_current_speed = 0.0; ++ int m_estimated_time = 0; + + QString m_src_uri; + QString m_dest_uri; +@@ -201,7 +208,9 @@ private: + int m_current_count = 1; + int m_update_count = 0; + quint64 m_total_size = 0; +- qint32 m_current_size = 0; ++ qint64 m_current_size = 0; ++ qint64 m_start_time = 0; ++ qint64 m_last_size = 0; + + bool m_pause = false; + +@@ -241,7 +250,7 @@ Q_SIGNALS: + + public Q_SLOTS: + void cancelld(); +- void updateValue (QString&, QIcon&, double); ++ void updateValue (QString&, QIcon&, double, double, int); + + private: + bool m_sync = false; +@@ -316,6 +325,8 @@ private: + float m_move_x = 0.5; + bool m_stopping = false; + float m_current_value = 0.0; ++ float m_current_speed = 0.0; ++ QString m_current_estimated_time = tr("Calculating time"); + QString m_file_name = tr("starting ..."); + QIcon m_icon = QIcon::fromTheme("text"); + }; +diff --git a/libpeony-qt/file-operation/file-operation.cpp b/libpeony-qt/file-operation/file-operation.cpp +index 91dfe92..9d2c574 100644 +--- a/libpeony-qt/file-operation/file-operation.cpp ++++ b/libpeony-qt/file-operation/file-operation.cpp +@@ -188,6 +188,24 @@ void FileOperation::fileSync(QString srcFile, QString destDir) + } + } + ++bool FileOperation::syncDestUri(const QString &destUri) ++{ ++ bool ret = false; ++ g_autoptr (GFile) ddir = g_file_new_for_uri (destUri.toUtf8().constData()); ++ char * path = g_file_get_path(ddir); ++ operationStartSnyc(); ++ QProcess p; ++ p.start(QString("/usr/bin/sync -f %1").arg(path)); ++ ret = p.waitForFinished(-1); ++ if (p.exitCode() == 0) { ++ qDebug() << "sync completed successfully"; ++ } else { ++ qDebug() << "sync failed with exit code:" << p.exitCode(); ++ } ++ g_free(path); ++ return ret; ++} ++ + void FileOperation::notifyFileWatcherOperationFinished() + { + if (!qApp->allWidgets().isEmpty()) { +@@ -232,6 +250,43 @@ void FileOperation::sendSrcAndDestUrisOfCopyDspsFiles() + qDebug()<<"fail to send source and dest uris of copy!"; + } + ++bool FileOperation::queryDirIsReadOnlyFS(const QString &dirUri, bool defaultResult, bool isUdfBurnWork, bool *writeable) ++{ ++ // udf 刻录操作默认可写 ++ if (isUdfBurnWork) { ++ if (writeable) ++ *writeable = true; ++ return false; ++ } ++ ++ g_autoptr (GFile) dest_dir_file = g_file_new_for_uri(dirUri.toUtf8().constData()); ++ g_autoptr (GFileInfo) dest_dir_info = g_file_query_info(dest_dir_file, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, G_FILE_QUERY_INFO_NONE, nullptr, nullptr); ++ if (g_file_info_has_attribute(dest_dir_info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) { ++ if (writeable) { ++ *writeable = true; ++ } ++ if (!g_file_info_get_attribute_boolean(dest_dir_info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) { ++ if (writeable) { ++ *writeable = false; ++ } ++ // 如果目录不可写,创建临时文件进行错误码匹配 ++ g_autoptr (GFile) temp_file = g_file_resolve_relative_path(dest_dir_file, "peony-template-file"); ++ GError *err = nullptr; ++ if (!g_file_create(temp_file, G_FILE_CREATE_NONE, nullptr, &err)) { ++ bool is_erofs = g_error_matches(err, G_IO_ERROR, G_IO_ERROR_READ_ONLY); ++ g_error_free (err); ++ return is_erofs; ++ } ++ } ++ return defaultResult; ++ } else { ++ if (writeable) { ++ *writeable = true; ++ } ++ return defaultResult; ++ } ++} ++ + bool FileOperation::URISorter::operator()(const QString &uri1, const QString &uri2) const { + bool isFolder1 = FileUtils::isFileDirectory(uri1); + bool isFolder2 = FileUtils::isFileDirectory(uri2); +diff --git a/libpeony-qt/file-operation/file-operation.h b/libpeony-qt/file-operation/file-operation.h +index 6711d7a..6d20cf9 100644 +--- a/libpeony-qt/file-operation/file-operation.h ++++ b/libpeony-qt/file-operation/file-operation.h +@@ -347,6 +347,8 @@ Q_SIGNALS: + + void operationInfoMsgBox(const QString &uri); + ++ void operationUdfBurnRunning(const bool &state); ++ + void remoteFileEvent(int eventType, const QString &arg1, const QString &arg2); + + public Q_SLOTS: +@@ -357,6 +359,7 @@ protected: + bool nameIsValid (QString& uri); + bool makeFileNameValidForDestFS (QString& srcPath, QString& destPath, QString* newFileName); + void OperatorThreadPause(); ++ bool syncDestUri(const QString &destUri); + + GCancellableWrapperPtr getCancellable() { + return m_cancellable_wrapper; +@@ -371,6 +374,8 @@ protected: + /* 发送给dbus服务关于:复制dsps文件时将复制成功文件的原路径和目的路径通过发信号通知WPS,Link to story#11452 */ + void sendSrcAndDestUrisOfCopyDspsFiles(); + ++ static bool queryDirIsReadOnlyFS(const QString &dirUri, bool defaultResult = false, bool isUdfBurnWork = false, bool *writeable = nullptr); ++ + protected: + QAtomicInteger<bool> m_is_pause = false; + QStringList m_src_uris; +diff --git a/libpeony-qt/file-operation/file-operation.pri b/libpeony-qt/file-operation/file-operation.pri +index 87f0ffe..65deea8 100644 +--- a/libpeony-qt/file-operation/file-operation.pri ++++ b/libpeony-qt/file-operation/file-operation.pri +@@ -12,7 +12,9 @@ HEADERS += \ + $$PWD/file-node.h \ + $$PWD/file-operation-helper.h \ + $$PWD/file-operation-internal-dialog.h \ ++ $$PWD/file-operation-progress-bar-helper.h \ + $$PWD/shared-file-link-operation.h \ ++ $$PWD/file-properties-operation.h \ + $$PWD/xatom-helper.h \ + $$PWD/file-operation.h \ + $$PWD/file-node-reporter.h \ +@@ -38,7 +40,9 @@ SOURCES += \ + $$PWD/file-node.cpp \ + $$PWD/file-operation-helper.cpp \ + $$PWD/file-operation-internal-dialog.cpp \ ++ $$PWD/file-operation-progress-bar-helper.cpp \ + $$PWD/shared-file-link-operation.cpp \ ++ $$PWD/file-properties-operation.cpp \ + $$PWD/xatom-helper.cpp \ + $$PWD/file-operation.cpp \ + $$PWD/file-node-reporter.cpp \ +diff --git a/libpeony-qt/file-operation/file-properties-operation.cpp b/libpeony-qt/file-operation/file-properties-operation.cpp +new file mode 100644 +index 0000000..759f553 +--- /dev/null ++++ b/libpeony-qt/file-operation/file-properties-operation.cpp +@@ -0,0 +1,297 @@ ++/* ++ * Peony-Qt's Library ++ * ++ * Copyright (C) 2024, KylinSoft Co., Ltd. ++ * ++ * 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 3 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 General Public License ++ * along with this library. If not, see <https://www.gnu.org/licenses/>. ++ * ++ * Authors: Yue Lan <lanyue@kylinos.cn> ++ * ++ */ ++ ++#include "file-properties-operation.h" ++ ++#include <glib.h> ++#include <glib/gstdio.h> ++#include <fcntl.h> ++#include <sys/types.h> ++#include <sys/stat.h> ++ ++#include <QFile> ++#include <QTextStream> ++#include <QString> ++ ++using namespace Peony; ++ ++void appendToFileLineByLine(const QString &filePath, const QString &content) { ++ QFile file(filePath); ++ if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) { ++ QTextStream out(&file); ++ out << content << endl; // 追加内容并添加换行符 ++ } ++ file.close(); ++} ++ ++void deleteLineContainingTextLineByLine(const QString &filePath, const QString &textToDelete) { ++ QFile originalFile(filePath); ++ QString tempFilePath = filePath + ".tmp"; ++ QFile tempFile(tempFilePath); ++ ++ if (originalFile.open(QIODevice::ReadOnly | QIODevice::Text) && tempFile.open(QIODevice::WriteOnly | QIODevice::Text)) { ++ QTextStream in(&originalFile); ++ QTextStream out(&tempFile); ++ QString line; ++ while (!in.atEnd()) { ++ line = in.readLine(); ++ if (!line.contains(textToDelete)) { ++ out << line << endl; // 写入不包含特定文本的行 ++ } ++ } ++ } ++ ++ originalFile.close(); ++ tempFile.close(); ++ ++ // 删除原文件并将临时文件重命名为原文件 ++ originalFile.remove(); ++ tempFile.rename(filePath); ++} ++ ++FilePropertiesOperation::FilePropertiesOperation(const QStringList &uris, FilePropertiesOperation::Options options, bool setHidden, bool setReadOnly, QObject *parent) ++{ ++ m_uris = uris; ++ m_shouldSetHidden = setHidden; ++ m_shouldSetReadOnly = setReadOnly; ++ m_options = options; ++ m_reporter = new FileNodeReporter(this); ++ connect(m_reporter, &FileNodeReporter::nodeFound, this, [=](const QString &uri){ ++ Q_EMIT this->operationPreparedOne(uri, 0); ++ }); ++ m_info = std::make_shared<FileOperationInfo>(uris, uris, FileOperationInfo::Other); ++} ++ ++void FilePropertiesOperation::run() ++{ ++ Q_EMIT operationStarted(); ++ if (m_uris.isEmpty()) ++ Q_EMIT operationFinished(); ++ ++ QList<FileNode *> nodes; ++ if (m_options.testFlag(ChangeRecursively)) { ++ for (auto uri : m_uris) { ++ auto node = new FileNode(uri, nullptr, m_reporter); ++ node->findChildrenRecursively(); ++ nodes<<node; ++ } ++ Q_EMIT operationPrepared(); ++ ++ bool cancelled = isCancelled(); ++ for (auto node : nodes) { ++ setPropertiesRecursively(node, &cancelled); ++ } ++ ++ Q_EMIT operationFinished(); ++ } else { ++ for (auto uri : m_uris) { ++ auto node = new FileNode(uri, nullptr, nullptr); ++ nodes<<node; ++ Q_EMIT this->operationPreparedOne(uri, 0); ++ } ++ ++ for (auto node : nodes) { ++ if (isCancelled()) { ++ break; ++ } ++ ++ setPropertiesOne(node); ++ } ++ } ++ ++ Q_EMIT operationFinished(); ++ ++ for (auto node : nodes) { ++ delete node; ++ } ++} ++ ++std::shared_ptr<FileOperationInfo> FilePropertiesOperation::getOperationInfo() ++{ ++ return m_info; ++} ++ ++void FilePropertiesOperation::setPropertiesRecursively(FileNode *node, bool *cancelled) ++{ ++ if (*cancelled) { ++ return; ++ } ++ ++ if (isCancelled()) { ++ *cancelled = true; ++ return; ++ } ++ ++ if (node->isFolder()) { ++ for (auto childNode : *node->children()) { ++ setPropertiesRecursively(childNode, cancelled); ++ } ++ if (m_options.testFlag(ChangeHidden)) { ++ if (m_shouldSetHidden) { ++ //将该目录下所有非隐藏文件的名称写入.hidden中 ++ g_autoptr (GFile) folder = g_file_new_for_uri(node->uri().toUtf8().constData()); ++ g_autoptr (GFile) hidden_file = g_file_resolve_relative_path(folder, ".hidden"); ++ QStringList filenameList; ++ for (FileNode *childNode : *(node->children())) { ++ g_autoptr (GFile) child_file = g_file_new_for_uri(childNode->uri().toUtf8().constData()); ++ g_autofree gchar *child_file_basename = g_file_get_basename(child_file); ++ childNode->setDestFileName(child_file_basename); ++ if (!childNode->destBaseName().startsWith(".")) { ++ filenameList.append(childNode->destBaseName()); ++ } ++ } ++ g_autofree gchar *hidden_file_path = g_file_get_path(hidden_file); ++ QString contents = filenameList.join('\n'); ++ if (hidden_file_path) ++ g_file_set_contents(hidden_file_path, contents.toUtf8().constData(), -1, nullptr); ++ ++ //顶级目录需要隐藏自身 ++ if (!node->parent()) { ++ setPropertiesOne(node); ++ } ++ } else { ++ //删除该目录下的.hidden文件 ++ g_autoptr (GFile) folder = g_file_new_for_uri (node->uri().toUtf8().constData()); ++ g_autoptr (GFile) hidden_file = g_file_resolve_relative_path(folder, ".hidden"); ++ g_file_delete (hidden_file, nullptr, nullptr); ++ ++ //顶级目录取消自身隐藏 ++ if (!node->parent()) { ++ setPropertiesOne(node); ++ } ++ } ++ } ++ } else { ++ setPropertiesOne(node); ++ } ++ Q_EMIT this->FileProgressCallback(node->uri(), node->uri(), "", 0, 0); ++} ++ ++void FilePropertiesOperation::setPropertiesOne(FileNode *node) ++{ ++ if (!node->isFolder() && node->baseName() == ".hidden") { ++ // 跳过.hidden文件 ++ return; ++ } ++ ++ g_autoptr (GFile) file = g_file_new_for_uri(node->uri().toUtf8().constData()); ++ g_autofree gchar *child_file_basename = g_file_get_basename(file); ++ node->setDestFileName(child_file_basename); ++ node->setDestUri(node->uri()); ++ ++ GError *err = nullptr; ++ bool shouldManuallyChanged = false; ++ if (m_options.testFlag(ChangeHidden)) { ++ // 对于顶级节点,如果没有修改unix-mode,需要手动触发文件更新以隐藏选项便数据同步 ++ shouldManuallyChanged = !node->parent(); ++ if (m_options.testFlag(ChangeReadOnly)) { ++ shouldManuallyChanged = false; ++ } ++ if (m_shouldSetHidden) { ++ // FIXME: 兼容原来形式 ++ // FIXME: 优化性能,顶层节点如果在同一个目录下可以统一处理 ++ if (!node->parent()) { ++ // 递归处理的隐藏设置由上一级目录设置时处理,此处仅处理顶级节点 ++ g_autoptr (GFile) directory = g_file_get_parent(file); ++ g_autoptr (GFile) hidden_file = g_file_resolve_relative_path(directory, ".hidden"); ++ g_autofree gchar *hidden_file_path = g_file_get_path(hidden_file); ++ appendToFileLineByLine(hidden_file_path, node->destBaseName()); ++ } ++ } else { ++ // 兼容"."前缀取消隐藏 ++ if (node->destBaseName().startsWith(".")) { ++ auto destName = node->destBaseName(); ++ destName.remove(0, 1); ++ g_autoptr (GFile) new_file = g_file_set_display_name(file, destName.toUtf8().constData(), nullptr, &err); ++ if (!err) { ++ g_autofree gchar *new_uri = g_file_get_uri(new_file); ++ node->setDestUri(new_uri); ++ } else { ++ // FIXME: 与errorhandler交互 ++ qWarning()<<"set file properties op error:"<<node->destBaseName()<<err->message; ++ } ++ } else { ++ // FIXME: 优化性能,顶层节点如果在同一个目录下可以统一处理 ++ if (!node->parent()) { ++ // 递归处理的隐藏设置由上一级目录设置时处理,此处仅处理顶级节点 ++ g_autoptr (GFile) directory = g_file_get_parent(file); ++ g_autoptr (GFile) hidden_file = g_file_resolve_relative_path(directory, ".hidden"); ++ g_autofree gchar *hidden_file_path = g_file_get_path(hidden_file); ++ deleteLineContainingTextLineByLine(hidden_file_path, node->destBaseName()); ++ } ++ } ++ } ++ } ++ ++ if (m_options.testFlag(ChangeReadOnly) && !node->isFolder()) { ++ g_autoptr (GFile) file = g_file_new_for_uri(node->destUri().toUtf8().constData()); ++ g_autofree gchar *path = g_file_get_path(file); ++ if (!node->destUri().startsWith("file://")) { ++ mode_t mod = 0; ++ quint32 mode = 0; ++ g_autoptr (GError) error = NULL; ++ g_autoptr (GFileInfo) info = g_file_query_info(file, "unix::mode", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, &error); ++ if (g_file_info_has_attribute(info, G_FILE_ATTRIBUTE_UNIX_MODE)) { ++ mode = g_file_info_get_attribute_uint32(info, G_FILE_ATTRIBUTE_UNIX_MODE); ++ ++ if (m_shouldSetReadOnly) { ++ mode &= ~S_IWUSR; ++ mode &= ~S_IWGRP; ++ mode &= ~S_IWOTH; ++ } else { ++ mode |= S_IWUSR; ++ mode |= S_IWGRP; ++ mode |= S_IWOTH; ++ } ++ mod = mode; ++ g_file_set_attribute_uint32(file, G_FILE_ATTRIBUTE_UNIX_MODE, (guint32)mod, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); ++ } ++ } else { ++ guint32 unixMode = 0755; ++ struct stat file_stat; ++ int ret = stat(path, &file_stat); ++ if (ret == -1) { ++ qCritical()<<"failed to get file permission" ; ++ // 保护箱场景下path可能存在崩溃问题 ++ // qCritical()<<"failed to get file permission" << path; ++ } else { ++ unixMode = file_stat.st_mode; ++ } ++ ++ if (m_shouldSetReadOnly) { ++ unixMode &= 0555; ++ } else { ++ unixMode |= 0222; ++ } ++ if (path) { ++ g_chmod(path, unixMode); ++ } ++ } ++ } ++ ++ if (shouldManuallyChanged) { ++ //更新文件 ++ g_autoptr (GFile) new_file = g_file_new_for_uri(node->destUri().toUtf8().constData()); ++ g_autoptr (GFileInfo) info = g_file_query_info(new_file, G_FILE_ATTRIBUTE_TIME_ACCESS, G_FILE_QUERY_INFO_NONE, nullptr, nullptr); ++ g_file_set_attribute_uint64(new_file, G_FILE_ATTRIBUTE_TIME_ACCESS, g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_TIME_ACCESS), G_FILE_QUERY_INFO_NONE, nullptr, nullptr); ++ } ++} +diff --git a/libpeony-qt/file-operation/file-properties-operation.h b/libpeony-qt/file-operation/file-properties-operation.h +new file mode 100644 +index 0000000..a6624ce +--- /dev/null ++++ b/libpeony-qt/file-operation/file-properties-operation.h +@@ -0,0 +1,73 @@ ++/* ++ * Peony-Qt's Library ++ * ++ * Copyright (C) 2024, KylinSoft Co., Ltd. ++ * ++ * 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 3 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 General Public License ++ * along with this library. If not, see <https://www.gnu.org/licenses/>. ++ * ++ * Authors: Yue Lan <lanyue@kylinos.cn> ++ * ++ */ ++ ++#ifndef FILEPROPERTIESOPERATION_H ++#define FILEPROPERTIESOPERATION_H ++ ++#include <QObject> ++ ++#include "peony-core_global.h" ++ ++#include "file-operation.h" ++ ++#include "file-node-reporter.h" ++#include "file-node.h" ++ ++namespace Peony { ++ ++class PEONYCORESHARED_EXPORT FilePropertiesOperation : public FileOperation ++{ ++ Q_OBJECT ++public: ++ enum Mode { ++ NoOp = 0, ++ ChangeHidden = 1 << 0, ++ ChangeReadOnly = 1 << 1, ++ ChangeRecursively = 1 << 2 ++ }; ++ Q_ENUM (Mode) ++ Q_DECLARE_FLAGS (Options, Mode) ++ ++ explicit FilePropertiesOperation(const QStringList &uris, Options mode = NoOp, bool setHidden = false, bool setReadOnly = false, QObject *parent = nullptr); ++ ++ void run() override; ++ ++ std::shared_ptr<FileOperationInfo> getOperationInfo(); ++ ++protected: ++ void setPropertiesRecursively(FileNode *node, bool *cancelled); ++ void setPropertiesOne(FileNode *node); ++ ++private: ++ QStringList m_uris; ++ bool m_shouldSetHidden = false; ++ bool m_shouldSetReadOnly = false; ++ Options m_options; ++ ++ FileNodeReporter *m_reporter = nullptr; ++ ++ std::shared_ptr<FileOperationInfo> m_info; ++}; ++ ++} ++ ++#endif // FILEPROPERTIESOPERATION_H +diff --git a/libpeony-qt/file-operation/file-rename-operation.cpp b/libpeony-qt/file-operation/file-rename-operation.cpp +index 0e79b3b..6bd4da7 100644 +--- a/libpeony-qt/file-operation/file-rename-operation.cpp ++++ b/libpeony-qt/file-operation/file-rename-operation.cpp +@@ -24,6 +24,7 @@ + #include "file-operation-manager.h" + #include "file-utils.h" + #include "file-info.h" ++#include "file-node.h" + #include <gio/gdesktopappinfo.h> + #include <glib/gprintf.h> + #include <global-settings.h> +@@ -36,9 +37,14 @@ static QString set_desktop_name (QString file, QString& name, GError** error); + + using namespace Peony; + +-static QString handleDuplicate(QString name) ++static QString handleDuplicate(QString name, bool isFolder) + { +- return FileUtils::handleDuplicateName(name); ++ if (isFolder) { ++ return FileUtils::handleFolderName(name); ++ } else { ++ return FileUtils::handleDuplicateName(name); ++ } ++ + } + + FileRenameOperation::FileRenameOperation(QString uri, QString newName) +@@ -272,7 +278,7 @@ retry: + setAutoBackup(); + case BackupOne:{ + while (FileUtils::isFileExsit(g_file_get_uri(newFile.get()->get()))) { +- QString fileUri = handleDuplicate(FileUtils::getFileUri(newFile)); ++ QString fileUri = handleDuplicate(FileUtils::getFileUri(newFile), isFolder); + m_new_name = FileUtils::getUriBaseName(fileUri); + newFile = FileUtils::resolveRelativePath(parent, m_new_name); + getOperationInfo().get()->m_dest_dir_uri = FileUtils::getFileUri(newFile); +@@ -338,11 +344,23 @@ retry: + setHasError(false); + goto retry; + } ++ case TruncateOne: { ++ int respValut = except.respValue.value("cateType").toInt(); ++ QString uri = g_file_get_uri(newFile.get()->get()); ++ FileNode *node = new FileNode(uri, nullptr, nullptr); ++ node->setDestUri(uri); ++ node->truncateDestFileName(respValut); ++ m_new_name = node->destBaseName(); ++ newFile = FileUtils::resolveRelativePath(parent, m_new_name); ++ getOperationInfo().get()->m_dest_dir_uri = FileUtils::getFileUri(newFile); ++ setHasError(false); ++ delete node; ++ goto retry; ++ } + default: + break; + } + } +- + g_error_free(err); + } else { + successed = true; +@@ -437,3 +455,4 @@ static QString set_desktop_name (QString file, QString& name, GError** error) + + return oldName; + } ++ +diff --git a/libpeony-qt/file-operation/file-trash-operation.cpp b/libpeony-qt/file-operation/file-trash-operation.cpp +index 470f26d..3900d6e 100644 +--- a/libpeony-qt/file-operation/file-trash-operation.cpp ++++ b/libpeony-qt/file-operation/file-trash-operation.cpp +@@ -23,7 +23,7 @@ + #include "file-trash-operation.h" + #include "file-operation-manager.h" + #include "file-enumerator.h" +- ++#include "global-settings.h" + #include <QProcess> + #include <file-info-job.h> + #include <file-info.h> +@@ -48,13 +48,56 @@ void FileTrashOperation::run() + + if (m_is_search) { + m_info.get()->m_is_search = true; ++ } else { ++ auto srcUri = m_src_uris.isEmpty()? nullptr: m_src_uris.first(); ++ auto parentUri = FileUtils::getParentUri(srcUri); ++ if (queryDirIsReadOnlyFS(parentUri)) { ++ FileOperationError except; ++ except.dlgType = ED_WARNING; ++ except.errorType = ET_GIO; ++ except.srcUri = srcUri; ++ except.destDirUri = nullptr; ++ except.op = FileOpTrash; ++ except.title = tr("File trash error"); ++ QUrl srcUrl(except.srcUri); ++ except.errorStr = tr("Can not trash %1: Read-only file system").arg(srcUrl.fileName()); ++ errored(except); ++ setHasError(true); ++ Q_EMIT operationFinished(); ++ return; ++ } + } + ++ // #Bug #218579 【模块单元测试】【需求25097】【文件管理器】删除两个同名文件,第一次撤销时还原的是第一次删除的文件,再次点击撤销时,文管闪退 ++ // 由于gio trash逻辑对应用不可见,导致应用无法很好的处理撤销删除的操作,这里需要通过glib的改动配合处理此问题 ++ GVfs *defaultvfs = g_vfs_get_default(); ++ // 使用glib的缓存处理undo问题,不需要用原来遍历比较的方式,可以提高删除效率 ++ bool useCacheInGlib = GPOINTER_TO_INT (g_object_get_data(G_OBJECT (defaultvfs), "use-glib-trash-cache")); ++ + QSet<QString> trashBefore; +- FileEnumerator e; +- e.setEnumerateDirectory("trash:///"); +- e.enumerateSync(); +- QStringList lsBefore = e.getChildrenUris (); ++ QStringList lsBefore; ++// FileEnumerator e; ++// e.setEnumerateDirectory("trash:///"); ++// e.enumerateSync(); ++// QStringList lsBefore = e.getChildrenUris (); ++// trashBefore = lsBefore.toSet (); ++ ++ g_autoptr (GFile) trashroot = g_file_new_for_uri("trash:///"); ++ g_autoptr (GFileEnumerator) enumerator = g_file_enumerate_children(trashroot, G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); ++ if (enumerator && !useCacheInGlib) { ++ auto child_info = g_file_enumerator_next_file(enumerator, nullptr, nullptr); ++ while (child_info) { ++ auto child = g_file_enumerator_get_child(enumerator, child_info); ++ auto uri = g_file_get_uri(child); ++ lsBefore<<uri; ++ g_free(uri); ++ g_object_unref(child); ++ g_object_unref(child_info); ++ child_info = g_file_enumerator_next_file(enumerator, nullptr, nullptr); ++ } ++ } else { ++ m_info->m_dest_uris.clear(); ++ } + trashBefore = lsBefore.toSet (); + + //add file_count para for file trash progress +@@ -62,11 +105,13 @@ void FileTrashOperation::run() + //all file total size, should use changed the calculate way + quint64 total_size = 0; + const quint64 ONE_GIB_SIZE = 1024*1024*1024; ++ auto standardPaths = FileUtils::standardPathList(); + for (auto src : m_src_uris) { + // pre-check for trash special directory ++ QUrl srcUrl = src; + if (src == "file:///data/home" || src == "file:///data/usershare" || + src == "file:///data/root" || src == "file:///home" || +- FileUtils::isStandardPath(src) || src == "file://" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation)) { ++ standardPaths.contains(srcUrl.path()) || src == "file://" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation)) { + FileOperationError except; + except.srcUri = src; + except.destDirUri = tr("trash:///"); +@@ -258,6 +303,27 @@ retry: + quint64 time = QDateTime::currentMSecsSinceEpoch(); + auto info = FileInfo::fromUri(src); + info.get()->setProperty(TRASH_TIME, time); ++ ++ char *trashitem_escape_name = (char *)g_object_get_data(G_OBJECT (srcFile.get()->get()), "trash-escape-name"); ++ bool is_home_dir_trash = GPOINTER_TO_INT (g_object_get_data(G_OBJECT (srcFile.get()->get()), "is-home-dir-trash")); ++ QString trashedFileUri; ++ if (is_home_dir_trash) { ++ g_autoptr (GFile) trashedfile = g_file_get_child(trashroot, trashitem_escape_name); ++ g_autofree gchar *trashedfileuri = g_file_get_uri(trashedfile); ++ trashedFileUri = trashedfileuri; ++ } else { ++ // 根据gvfs trash的uri构造规则,非家目录挂载点文件在回收站内的uri做了二次编码,故需要再次编码trashitem_escape_name ++ g_autofree gchar *trashitem_escape_name2 = g_uri_escape_string(trashitem_escape_name, ":/\\", false); ++ g_autoptr (GFile) trashedfile = g_file_get_child(trashroot, trashitem_escape_name2); ++ g_autofree gchar *trashedfileuri = g_file_get_uri(trashedfile); ++ trashedFileUri = trashedfileuri; ++ } ++ ++ ++ if (!trashedFileUri.isEmpty()) { ++ useCacheInGlib = true; ++ m_info.get()->m_dest_uris<<trashedFileUri; ++ } + } + + Q_EMIT FileProgressCallback("trash:///", src, "", ++curSize, file_count); +@@ -265,7 +331,7 @@ retry: + + // fix dest uris in trash in FileOperationInfo + // fileinfo关联被删除的时间,然后枚举trash中displayname相同的文件,对比时间差最近的文件作为待恢复的文件 +- if (!isCancelled()) { ++ if (!isCancelled() && !useCacheInGlib) { + QSet<QString> trashAfter; + FileEnumerator e; + e.setEnumerateDirectory("trash:///"); +@@ -304,6 +370,28 @@ retry: + } + } + ++ bool trashMobileFile = GlobalSettings::getInstance()->getValue(TRASH_MOBILE_FILES).toBool(); ++ if (trashMobileFile) { ++ bool isMobileDevice = FileUtils::isMobileDeviceFile(m_src_uris.first()); ++ if (isMobileDevice) { ++ auto uri = FileUtils::getParentUri(m_src_uris.first()); ++ if (! uri.isEmpty()) { ++ g_autoptr (GFile) ddir = g_file_new_for_uri (uri.toUtf8().constData()); ++ char * path = g_file_get_path(ddir); ++ operationStartSnyc(); ++ QProcess p; ++ p.start(QString("/usr/bin/sync -f %1").arg(path)); ++ p.waitForFinished(-1); ++ if (p.exitCode() == 0) { ++ qDebug() << "sync completed successfully"; ++ } else { ++ qDebug() << "sync failed with exit code:" << p.exitCode(); ++ } ++ g_free(path); ++ } ++ } ++ } ++ + Q_EMIT operationFinished(); + //notifyFileWatcherOperationFinished(); + } +diff --git a/libpeony-qt/file-operation/file-untrash-operation.cpp b/libpeony-qt/file-operation/file-untrash-operation.cpp +index a156712..87c9c55 100644 +--- a/libpeony-qt/file-operation/file-untrash-operation.cpp ++++ b/libpeony-qt/file-operation/file-untrash-operation.cpp +@@ -387,11 +387,26 @@ void FileUntrashOperation::run() + // 3. undo the operation in desktop application. + // in this case trashedFileLocaledUri is empty, and could not get + // the responding info. so I add a checkment to avoid the case happened. +- originUri = "file://" + metaInfo.get()->getMetaInfoString("orig-path"); ++ QString origPath = metaInfo->getMetaInfoString("orig-path"); ++ if (origPath.isEmpty()) { ++ qWarning() << "invalid file meta info orig-path" << trashedFileLocaledUri; ++ } else { ++ g_autofree gchar *uri = g_filename_to_uri(origPath.toUtf8().constData(), nullptr, nullptr); ++ originUri = uri; ++ } + } else { + qWarning()<<"invalid file meta info orig-path"<<trashedFileLocaledUri; + } + } ++ if (originUri.isEmpty()) { ++ FileOperationError except; ++ except.errorCode = G_IO_ERROR_NOT_FOUND; ++ except.errorStr = tr("Can not find trashed file %1, might be restored or removed.").arg(uri); ++ except.errorType = ET_GIO; ++ except.dlgType = ED_WARNING; ++ errored(except); ++ goto l_out; ++ } + + auto file = wrapGFile(g_file_new_for_uri(uri.toUtf8().constData())); + auto destFile = wrapGFile(g_file_new_for_uri(originUri.toUtf8().constData())); +diff --git a/libpeony-qt/file-utils.cpp b/libpeony-qt/file-utils.cpp +index fb7eac7..2525b5e 100644 +--- a/libpeony-qt/file-utils.cpp ++++ b/libpeony-qt/file-utils.cpp +@@ -23,8 +23,11 @@ + #include "file-utils.h" + #include "file-info.h" + #include "file-info-job.h" ++#include "file-enumerator.h" + #include "volume-manager.h" + #include "linux-pwd-helper.h" ++#include "volumeManager.h" ++#include "global-fstabdata.h" + #include <QUrl> + #include <QFileInfo> + #include <QFileInfoList> +@@ -43,6 +46,8 @@ + #include <gio/gunixmounts.h> + #include <QCoreApplication> + #include <QThread> ++#include <QFile> ++#include <QTextStream> + + using namespace Peony; + +@@ -217,6 +222,30 @@ QString FileUtils::handleDuplicateName(const QString& uri) + return handledName; + } + ++QString FileUtils::handleFolderName(const QString &folderName) ++{ ++ QRegExp regExpNum("\\(\\d+\\)"); ++ QRegExp regExp (QString("\\ -\\ %1\\(\\d+\\)(\\.[0-9a-zA-Z\\.]+|)$").arg(QObject::tr("duplicate"))); ++ QString handledName = nullptr; ++ QString name = folderName; ++ if (name.contains(regExp)) { ++ int num = 0; ++ QString numStr = ""; ++ QString ext = regExp.cap(0); ++ if (ext.contains(regExpNum)) { ++ numStr = regExpNum.cap(0); ++ } ++ numStr.remove(0, 1); ++ numStr.chop(1); ++ num = numStr.toInt(); ++ ++num; ++ handledName = name.replace(regExp, ext.replace(regExpNum, QString("(%1)").arg(num))); ++ } else { ++ handledName = name + QString(" - %1(1)").arg(QObject::tr("duplicate")); ++ } ++ return handledName; ++} ++ + QString FileUtils::handleDesktopFileName(const QString& uri, const QString& displayName) + { + //no need self handle, add return to fix bug#72642 +@@ -336,6 +365,8 @@ QString FileUtils::getNonSuffixedBaseNameFromUri(const QString &uri) + suffix == ".sit") { + int secondIndex = suffixedBaseName.lastIndexOf('.'); + suffixedBaseName.chop(suffixedBaseName.size() - secondIndex); ++ } else { ++ suffixedBaseName.chop(suffixedBaseName.size() - index); + } + #else + suffixedBaseName.chop(suffixedBaseName.size() - index); +@@ -353,8 +384,7 @@ QString FileUtils::getFileDisplayName(const QString &uri) + return QObject::tr("data"); + //fix bug#47597, show as root.link issue. 125255, file system show tip "/" issue + if (uri == "file:///") +- return QObject::tr("File System"); +- ++ return QObject::tr("System Disk"); + //fix bug#139600,替换windows共享名称, “172.17.123.173上的Windows共享” 显示为 "172.17.123.173上的共享" + bool isSmbPath = uri.startsWith("smb://"); + QString showName = fileInfo.get()->displayName(); +@@ -366,14 +396,14 @@ QString FileUtils::getFileDisplayName(const QString &uri) + return showName; + } + } +- if(uri.startsWith("label://")){/* 标记模式uri的displayName */ +- if("label:///" == uri){ +- showName = QObject::tr("label"); +- }else{ +- showName = uri.section("/", -1,-1).replace("?schema=file",""); +- } +- return showName; +- } ++// if(uri.startsWith("label://")){/* 标记模式uri的displayName */ ++// if("label:///" == uri){ ++// showName = QObject::tr("label"); ++// }else{ ++// showName = uri.section("/", -1,-1).replace("?schema=file",""); ++// } ++// return showName; ++// } + + return fileInfo.get()->displayName(); + } +@@ -664,6 +694,58 @@ bool FileUtils::isFileExsit(const QString &uri) + return exist; + } + ++/* ++ * 1.用于默认安装创建的数据盘场景; ++ * 2.默认会有绑定挂载目录/data/root, /data/home, /data/usershare; ++ * 3.自定义安装的数据盘,没有绑定挂载,不适用; ++ * 4.除默认目录之外,还存在其他文件夹或文件,才属于用户数据; ++ * 5./data/.Trash-*,/data/lost+found此类目录也属于默认目录; ++*/ ++bool FileUtils::isDataBlockHasUserFile() ++{ ++ QString configFilePath = "/etc/xdg/peony-data.conf"; ++ if (! QFile::exists(configFilePath)) ++ return true; ++ ++ QFile file(configFilePath); ++ if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { ++ QTextStream in(&file); ++ QString line = in.readLine(); ++ // 判断标识是否为true, 是的话,存在用户数据,如果是false, 则是干净的数据盘,可以使用新方案 ++ if (line != "true") ++ return false; ++ file.close(); ++ } else { ++ qWarning() << "open /etc/xdg/peony-data.conf failed"; ++ } ++ ++ return true; ++ ++ //之前的阻塞方法,文件量大时会造成卡顿,屏蔽 ++// Peony::FileEnumerator e; ++// e.setEnumerateDirectory("file:///data"); ++// e.enumerateSync(); ++// QStringList systemUris; ++// systemUris<< "file:///data/root"<< "file:///data/home" <<"file:///data/usershare" << "file:///data/lost+found"; ++// qDebug() << "/data children:"<<e.getChildrenUris().length(); ++// if (e.getChildrenUris().length() <= systemUris.length()) { ++// return false; ++// } else { ++// for (auto fileInfo : e.getChildren()) { ++// QString childUri = fileInfo->uri(); ++// qDebug() << "data childUri:"<<childUri; ++// //确认别的子文件不是回收站目录,用户id不同,回收站目录名不同,则可以跳转/data目录 ++// if (! systemUris.contains(childUri) && ! childUri.startsWith("file:///data/.Trash") && ++// childUri != "file:///home" && childUri != "file:///root"){ ++// return true; ++// } ++// } ++// } ++ ++// return false; ++} ++ ++ + const QStringList FileUtils::toDisplayUris(const QStringList &args) + { + QStringList uris; +@@ -715,7 +797,7 @@ bool FileUtils::queryVolumeInfo(const QString &volumeUri, QString &volumeName, Q + + GFile *file = g_file_new_for_uri(volumeUri.toUtf8().constData()); + GFileInfo *info = g_file_query_info(file, +- G_FILE_ATTRIBUTE_MOUNTABLE_UNIX_DEVICE_FILE","G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, ++ G_FILE_ATTRIBUTE_MOUNTABLE_UNIX_DEVICE_FILE "," G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + nullptr, + nullptr); +@@ -1180,6 +1262,72 @@ bool FileUtils::isRemoteServerUri(const QString &uri) + return false; + } + ++bool FileUtils::isCompressedFile(const QString &contentType) ++{ ++ // Check if the content type is in the list of compressed file types ++ return getCompressedTypes().contains(contentType); ++} ++ ++const QStringList &FileUtils::getCompressedTypes() ++{ ++ /** ++ * @brief Static list of MIME types for compressed files. ++ * ++ * This list is initialized only once and contains MIME types ++ * for various compressed file formats. ++ */ ++ static const QStringList compressedTypes = { ++ "application/zip", "application/x-rar-compressed", "application/x-gzip", ++ "application/gzip", "application/x-bzip2", "application/x-7z-compressed", ++ "application/x-xz", "application/x-lzma", "application/x-lzip", ++ "application/x-lzop", "application/x-snappy-framed", "application/zstd", ++ "application/x-compress", "application/x-compressed", "application/x-zip-compressed", ++ "application/x-gtar", "application/x-tar", "application/x-bzip", ++ "application/x-lzh", "application/x-lha", "application/vnd.rar", ++ "application/x-ace-compressed", "application/x-astrotite-afa", "application/x-alz-compressed", ++ "application/x-arj", "application/x-b1", "application/vnd.ms-cab-compressed", ++ "application/x-cfs-compressed", "application/x-dar", "application/x-dgc-compressed", ++ "application/x-apple-diskimage", "application/x-gca-compressed", "application/java-archive", ++ "application/x-lzx", "application/x-lzh-compressed", "application/x-stuffit", ++ "application/x-stuffitx", "application/x-par2", "application/x-rar", ++ "application/x-sit", "application/x-squashfs-image", "application/x-xar", ++ "application/x-zoo" ++ }; ++ return compressedTypes; ++} ++ ++bool FileUtils::isExecuteTargetUribyTrashUri(const QUrl& url, FileInfo * fileInfo) ++{ ++ if (QFileInfo::exists(url.path().toUtf8()) && fileInfo->property("enable_trash_target").toBool()) ++ { ++ GFile * _file = g_file_new_for_uri(url.toString().toUtf8().constData()); ++ GError *err = nullptr; ++ bool _can_excute = false; ++ auto gFileInfo = g_file_query_info(_file, ++ "standard::*," "time::*," "access::*," "mountable::*," "metadata::*," "trash::*," G_FILE_ATTRIBUTE_ID_FILE, ++ G_FILE_QUERY_INFO_NONE, ++ NULL, ++ &err); ++ ++ if (err) { ++ qDebug()<<__func__<<__LINE__<<err->code<<err->message; ++ g_error_free(err); ++ }else{ ++ if (g_file_info_has_attribute(gFileInfo, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE)) { ++ _can_excute = g_file_info_get_attribute_boolean(gFileInfo, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE); ++ } else { ++ _can_excute = true; ++ } ++ } ++ ++ g_object_unref(_file); ++ if (_can_excute) ++ return true; ++ } ++ ++ return false; ++} ++ + bool FileUtils::isEmptyDisc(const QString &unixDevice) + { + if (unixDevice.isEmpty()) //没有设备时不做后续处理 +@@ -1343,6 +1491,11 @@ QString FileUtils::getFsTypeFromFile(const QString &fileUri) + { + QString fsType = "ext"; + ++ QString homeDir = "file://" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation); ++ ++ if (fileUri.startsWith("filesafe:///") || fileUri.startsWith(homeDir + "/.box")) ++ return "ecryptfs"; ++ + g_autoptr (GFile) file = g_file_new_for_uri(fileUri.toUtf8().constData()); + g_autoptr (GMount) mount = g_file_find_enclosing_mount(file, nullptr, nullptr); + if (!mount) +@@ -1474,6 +1627,45 @@ QString FileUtils::updateFileIconName(const QString &uri, bool checkValid) + return icon_name; + } + ++bool FileUtils::isSearchFilesParentWriteable(const QStringList &selectUris, bool isSearch) ++{ ++ /* 经过讨论,对于搜索路径,判断每个选中文件的父目录的可写权限,都有可写权限时才返回true,反之则为false */ ++ bool canWrite = true; ++ if(isSearch && !selectUris.isEmpty()){ ++ for (auto selectUri : selectUris) { ++ auto fileInfo = FileInfo::fromUri(selectUri); ++ if(!fileInfo->canRename()){ ++ canWrite = false; ++ break; ++ } ++ } ++ } ++ return canWrite; ++} ++ ++bool FileUtils::isMountMatchFstab(GVolume *volume, const QString &mountPoint) ++{ ++ if (G_IS_VOLUME(volume)) { ++ Experimental_Peony::Volume *vol = new Experimental_Peony::Volume(volume); ++ if(!Peony::GlobalFstabData::getInstance()->getUuidState()){ ++ if(Peony::GlobalFstabData::getInstance()->isMountPoints(vol->device(), mountPoint)){ ++ return true; ++ } ++ }else{ ++ if(Peony::GlobalFstabData::getInstance()->isMountPoints(vol->uuid(), mountPoint)){ ++ return true; ++ } ++ } ++ ++ if (vol) { ++ delete vol; ++ } ++ } ++ ++ return false; ++ ++} ++ + QString FileUtilsPrivate::getFileIconName(const QString &uri) + { + if (nullptr == uri) return ""; +diff --git a/libpeony-qt/file-utils.h b/libpeony-qt/file-utils.h +index ca68dd4..2ba3936 100644 +--- a/libpeony-qt/file-utils.h ++++ b/libpeony-qt/file-utils.h +@@ -33,7 +33,7 @@ + #include <QString> + + namespace Peony { +- ++class FileInfo; + class PEONYCORESHARED_EXPORT FileUtils + { + public: +@@ -53,6 +53,7 @@ public: + * @return handled name + */ + BLOCKING static QString handleDuplicateName (const QString& uri); ++ BLOCKING static QString handleFolderName (const QString& folderName); + BLOCKING static bool getFileHasChildren(const GFileWrapperPtr &file); + BLOCKING static bool getFileIsFolder(const GFileWrapperPtr &file); + BLOCKING static bool getFileIsFolder(const QString &uri); +@@ -85,6 +86,7 @@ public: + + + BLOCKING static bool isFileExsit(const QString &uri); ++ BLOCKING static bool isDataBlockHasUserFile(); + + NO_BLOCKING static const QStringList toDisplayUris(const QStringList &args); + +@@ -124,7 +126,31 @@ public: + static bool isLongNameFileOfNotDel2Trash(const QString &fileUri);/* 判断是否是无法删除到回收站的长文件名文件 */ + static QString getActualDirFromSearchUri(const QString& searchUri); + static QString updateFileIconName(const QString &uri, bool checkValid = false); ++ static bool isSearchFilesParentWriteable(const QStringList &selectUris, bool isSearch);/* 选中的搜索出来的文件(夹),其父目录是否有写权限 */ ++ static bool isMountMatchFstab(GVolume* volume, const QString &mountPoint); ++ ++ /** ++ * @brief Determines if the given file content type is a compressed file. ++ * ++ * This method checks the MIME type of the file against a predefined list ++ * of compressed file types. ++ * ++ * @param contentType The contentType to be checked. ++ * @return true if the contentType is compressed, false otherwise. ++ */ ++ NO_BLOCKING static bool isCompressedFile(const QString &contentType); ++ /** ++ * @brief Gets the list of compressed file MIME types. ++ * ++ * This method returns a static QStringList containing MIME types ++ * associated with various compressed file formats. ++ * ++ * @return A const reference to the QStringList of compressed file MIME types. ++ */ ++ NO_BLOCKING static const QStringList& getCompressedTypes(); + ++ static bool isExecuteTargetUribyTrashUri(const QUrl& url, FileInfo * fileInfo); ++ + private: + FileUtils(); + }; +diff --git a/libpeony-qt/file-watcher.cpp b/libpeony-qt/file-watcher.cpp +index 0a85574..3ab677d 100644 +--- a/libpeony-qt/file-watcher.cpp ++++ b/libpeony-qt/file-watcher.cpp +@@ -24,13 +24,14 @@ + #include "gerror-wrapper.h" + + #include "file-label-model.h" +- +-#include <QUrl> + #include "file-utils.h" + #include "file-operation-manager.h" + #include "file-info.h" + #include "volume-manager.h" + #include "volumeManager.h" ++#include "global-settings.h" ++ ++#include <QUrl> + #include <QDebug> + #include <QStandardPaths> + +@@ -150,26 +151,30 @@ void FileWatcher::startMonitor() + } + }); + +- connect(VolumeManager::getInstance(), &VolumeManager::fileUnmounted, this, [=](const QString &uri){ +- auto fixedUri = uri; +- if (m_uri == uri || m_target_uri == uri || m_uri + "/" == fixedUri) { +- Q_EMIT directoryUnmounted(uri); +- } +- }); +- connect(Experimental_Peony::VolumeManager::getInstance(), &Experimental_Peony::VolumeManager::signal_unmountFinished, this, [=](const QString &uri){ +- /* volume卸载完成跳转到计算机目录(只有进入volume内部目录,卸载时才跳转) */ +- auto fixedUri = uri; +- if (m_uri.contains(uri)||m_target_uri.contains(uri)|| m_uri + "/" == fixedUri) { +- Q_EMIT directoryUnmounted(uri); +- } +- }); ++ if(!GlobalSettings::getInstance()->isDesktopStartUp()){ ++ connect(VolumeManager::getInstance(), &VolumeManager::fileUnmounted, this, [=](const QString &uri){ ++ auto fixedUri = uri; ++ if (m_uri == uri || m_target_uri == uri || m_uri + "/" == fixedUri) { ++ Q_EMIT directoryUnmounted(uri); ++ } ++ }); ++ connect(Experimental_Peony::VolumeManager::getInstance(), &Experimental_Peony::VolumeManager::signal_unmountFinished, this, [=](const QString &uri){ ++ /* volume卸载完成跳转到计算机目录(只有进入volume内部目录,卸载时才跳转) */ ++ auto fixedUri = uri; ++ if (m_uri.contains(uri)||m_target_uri.contains(uri)|| m_uri + "/" == fixedUri) { ++ Q_EMIT directoryUnmounted(uri); ++ } ++ }); ++ } + } + + void FileWatcher::stopMonitor() + { + disconnect(FileLabelModel::getGlobalModel(), &FileLabelModel::fileLabelChanged, this, 0); +- disconnect(VolumeManager::getInstance(), &VolumeManager::fileUnmounted, this, 0); +- disconnect(Experimental_Peony::VolumeManager::getInstance(), &Experimental_Peony::VolumeManager::signal_unmountFinished, this, 0); ++ if(!GlobalSettings::getInstance()->isDesktopStartUp()){ ++ disconnect(VolumeManager::getInstance(), &VolumeManager::fileUnmounted, this, 0); ++ disconnect(Experimental_Peony::VolumeManager::getInstance(), &Experimental_Peony::VolumeManager::signal_unmountFinished, this, 0); ++ } + if (m_file_handle > 0) { + g_signal_handler_disconnect(m_monitor, m_file_handle); + m_file_handle = 0; +@@ -279,6 +284,9 @@ void FileWatcher::file_changed_callback(GFileMonitor *monitor, + char *uri = g_file_get_uri(file); + //qDebug()<<uri; + Q_EMIT p_this->fileChanged(uri); ++ if(QString(uri).startsWith("label:///")){ ++ Q_EMIT p_this->directoryAttrChanged(uri); ++ } + g_free(uri); + break; + } +diff --git a/libpeony-qt/file-watcher.h b/libpeony-qt/file-watcher.h +index fe3e3cd..81413da 100644 +--- a/libpeony-qt/file-watcher.h ++++ b/libpeony-qt/file-watcher.h +@@ -90,6 +90,7 @@ public: + Q_SIGNALS: + void locationChanged(const QString &oldUri, const QString &newUri); + void directoryDeleted(const QString &uri); ++ void directoryAttrChanged(const QString &uri); + void directoryUnmounted(const QString &uri); + void fileCreated(const QString &uri); + void fileDeleted(const QString &uri); +diff --git a/libpeony-qt/global-fstabdata.h b/libpeony-qt/global-fstabdata.h +index 8a412a4..a16818c 100644 +--- a/libpeony-qt/global-fstabdata.h ++++ b/libpeony-qt/global-fstabdata.h +@@ -34,12 +34,12 @@ class PEONYCORESHARED_EXPORT GlobalFstabData : public QObject + Q_OBJECT + public: + static GlobalFstabData *getInstance(); +- explicit GlobalFstabData(QObject *parent = nullptr); + bool isMountPoints(const QString& name, const QString& mountPoint); + bool getUuidState(); + bool isEmptyList(); + + private: ++ explicit GlobalFstabData(QObject *parent = nullptr); + const QList<fstabData*> readFstabFile(); + QList<fstabData*> m_fstabDataLists; + QFileSystemWatcher *m_fstab_data_watcher; +diff --git a/libpeony-qt/global-settings.cpp b/libpeony-qt/global-settings.cpp +index b090c70..a6abda2 100644 +--- a/libpeony-qt/global-settings.cpp ++++ b/libpeony-qt/global-settings.cpp +@@ -41,6 +41,8 @@ + #include <kysdk/kysdk-system/libkysysinfo.h> + #endif + ++#include <QDBusInterface> ++ + using namespace Peony; + + static GlobalSettings *global_instance = nullptr; +@@ -73,6 +75,15 @@ GlobalSettings::GlobalSettings(QObject *parent) : QObject(parent) + m_cache.insert(HOME_ICON_VISIBLE, true); + m_cache.insert(TRASH_ICON_VISIBLE, true); + m_cache.insert(COMPUTER_ICON_VISIBLE, true); ++ ++ //story 28073, control the right menu open terminal option ++ m_cache.insert(SHOW_OPEN_TERMINAL, true); ++ //story 28077, control start peony or show peony UI ++ m_cache.insert(ENABLE_START_PEONY, true); ++ //story 28081, control double click desktop files in desktop ++ m_cache.insert(ENABLE_DOUBLE_CLICK_DESKTOP, true); ++ //story 28083, control file operation of shortcut keys ++ m_cache.insert(ENABLE_SHORTCUT_KEYS, true); + if (QGSettings::isSchemaInstalled("org.ukui.peony.settings")) { + connect(m_peonyGSettings, &QGSettings::changed, this, [=] (const QString &key) { + m_cache.remove(key); +@@ -84,6 +95,58 @@ GlobalSettings::GlobalSettings(QObject *parent) : QObject(parent) + m_cache.remove(key); + m_cache.insert(key, m_peonyGSettings->get(key)); + } ++ ++ connect(m_peonyGSettings, &QGSettings::changed, this, [=] (const QString &key) { ++ if (key == SHOW_OPEN_TERMINAL) { ++ m_cache.remove(SHOW_OPEN_TERMINAL); ++ m_cache.insert(SHOW_OPEN_TERMINAL, m_peonyGSettings->get(SHOW_OPEN_TERMINAL)); ++ } ++ Q_EMIT this->valueChanged(key); ++ }); ++ ++ if (m_peonyGSettings->keys().contains(SHOW_OPEN_TERMINAL)) { ++ m_cache.remove(SHOW_OPEN_TERMINAL); ++ m_cache.insert(SHOW_OPEN_TERMINAL, m_peonyGSettings->get(SHOW_OPEN_TERMINAL)); ++ } ++ ++ connect(m_peonyGSettings, &QGSettings::changed, this, [=] (const QString &key) { ++ if (key == ENABLE_START_PEONY) { ++ m_cache.remove(ENABLE_START_PEONY); ++ m_cache.insert(ENABLE_START_PEONY, m_peonyGSettings->get(ENABLE_START_PEONY)); ++ } ++ Q_EMIT this->valueChanged(key); ++ }); ++ ++ if (m_peonyGSettings->keys().contains(ENABLE_START_PEONY)) { ++ m_cache.remove(ENABLE_START_PEONY); ++ m_cache.insert(ENABLE_START_PEONY, m_peonyGSettings->get(ENABLE_START_PEONY)); ++ } ++ ++ connect(m_peonyGSettings, &QGSettings::changed, this, [=] (const QString &key) { ++ if (key == ENABLE_DOUBLE_CLICK_DESKTOP) { ++ m_cache.remove(ENABLE_DOUBLE_CLICK_DESKTOP); ++ m_cache.insert(ENABLE_DOUBLE_CLICK_DESKTOP, m_peonyGSettings->get(ENABLE_DOUBLE_CLICK_DESKTOP)); ++ } ++ Q_EMIT this->valueChanged(key); ++ }); ++ ++ if (m_peonyGSettings->keys().contains(ENABLE_DOUBLE_CLICK_DESKTOP)) { ++ m_cache.remove(ENABLE_DOUBLE_CLICK_DESKTOP); ++ m_cache.insert(ENABLE_DOUBLE_CLICK_DESKTOP, m_peonyGSettings->get(ENABLE_DOUBLE_CLICK_DESKTOP)); ++ } ++ ++ connect(m_peonyGSettings, &QGSettings::changed, this, [=] (const QString &key) { ++ if (key == ENABLE_SHORTCUT_KEYS) { ++ m_cache.remove(ENABLE_SHORTCUT_KEYS); ++ m_cache.insert(ENABLE_SHORTCUT_KEYS, m_peonyGSettings->get(ENABLE_SHORTCUT_KEYS)); ++ } ++ Q_EMIT this->valueChanged(key); ++ }); ++ ++ if (m_peonyGSettings->keys().contains(ENABLE_SHORTCUT_KEYS)) { ++ m_cache.remove(ENABLE_SHORTCUT_KEYS); ++ m_cache.insert(ENABLE_SHORTCUT_KEYS, m_peonyGSettings->get(ENABLE_SHORTCUT_KEYS)); ++ } + } + + m_cache.insert(TRASH_MOBILE_FILES, false); +@@ -173,7 +236,7 @@ GlobalSettings::GlobalSettings(QObject *parent) : QObject(parent) + connect(m_peony_gsettings, &QGSettings::changed, this, [=](const QString &key) { + bool sendChanged = false; + if ((SHOW_HIDDEN_PREFERENCE == key) || (SHOW_FILE_EXTENSION == key) || key == DISPLAY_STANDARD_ICONS || key == USE_GLOBAL_DEFAULT_SORTING || +- SHOW_CREATE_TIME == key || SHOW_RELATIVE_DATE == key) { ++ SHOW_CREATE_TIME == key || SHOW_RELATIVE_DATE == key || key == DESKTOP_USE_AUTO_LAYOUT) { + if (m_cache.value(key) != m_peony_gsettings->get(key).toBool()) + { + m_cache.remove(key); +@@ -200,6 +263,14 @@ GlobalSettings::GlobalSettings(QObject *parent) : QObject(parent) + } + m_showCreateTime = m_cache.value(SHOW_CREATE_TIME).toBool(); + m_showRelativeTime = m_cache.value(SHOW_RELATIVE_DATE).toBool(); ++ ++ if (m_peony_gsettings->keys().contains(DESKTOP_USE_AUTO_LAYOUT)) { ++ m_cache.remove(DESKTOP_USE_AUTO_LAYOUT); ++ m_cache.insert(DESKTOP_USE_AUTO_LAYOUT, m_peony_gsettings->get(DESKTOP_USE_AUTO_LAYOUT).toBool()); ++ } else { ++ m_cache.remove(DESKTOP_USE_AUTO_LAYOUT); ++ m_cache.insert(DESKTOP_USE_AUTO_LAYOUT, false); ++ } + } + + m_cache.insert(SIDEBAR_BG_OPACITY, 100); +@@ -303,6 +374,8 @@ GlobalSettings::GlobalSettings(QObject *parent) : QObject(parent) + } + #endif + initDateFormatDBus(); ++ ++ initManageControl(); + } + + GlobalSettings::~GlobalSettings() +@@ -396,8 +469,137 @@ void GlobalSettings::getDualScreenMode() + } + } + ++void GlobalSettings::initManageControl() ++{ ++ // Get user name and construct config path ++ const char* user = getenv("USER"); ++ if (user) { ++ m_currentConfigPath = constructConfigPath(QString(user)); ++ loadJsonConfig(m_currentConfigPath); ++ } ++ ++ initDBus(); ++} ++ ++bool GlobalSettings::loadJsonConfig(const QString &configPath) ++{ ++ // Clear JSON cache if config file doesn't exist or can't be opened ++ QFile file(configPath); ++ if (!file.exists() || !file.open(QFile::ReadOnly)) { ++ qWarning() << "Failed to open config file:" << configPath; ++ m_jsonCache.clear(); ++ return false; ++ } ++ ++ QJsonParseError error; ++ QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error); ++ file.close(); ++ ++ // Clear cache if JSON parsing fails ++ if (error.error != QJsonParseError::NoError) { ++ qWarning() << "Failed to parse JSON config:" << error.errorString(); ++ m_jsonCache.clear(); ++ return false; ++ } ++ ++ const QJsonObject root = doc.object(); ++ const QJsonObject peonySettings = root[PEONY_SETTINGS_KEY].toObject(); ++ ++ // Clear cache if no valid settings found ++ if (peonySettings.isEmpty()) { ++ m_jsonCache.clear(); ++ return false; ++ } ++ ++ // Update cache with new settings ++ updateConfigCache(peonySettings); ++ return true; ++} ++ ++void GlobalSettings::initDBus() ++{ ++ QDBusConnection::systemBus().connect( ++ "", ++ "/securityConfig", ++ "com.kylin.ukui.SettingsDaemon.interface", ++ "configChanged", ++ this, ++ SLOT(onConfigFileChanged(QString, QString, QString)) ++ ); ++} ++ ++QString GlobalSettings::convertJsonKeyToInternalKey(const QString &jsonKey) ++{ ++ QStringList parts = jsonKey.split(QRegExp("[-_]")); ++ QString result = parts[0]; ++ for (int i = 1; i < parts.size(); ++i) { ++ if (!parts[i].isEmpty()) { ++ result += parts[i][0].toUpper() + parts[i].mid(1); ++ } ++ } ++ return result; ++} ++ ++void GlobalSettings::updateConfigCache(const QJsonObject &peonySettings) ++{ ++ // Create a set of new configuration keys ++ QSet<QString> newKeys; ++ ++ // Process new and modified settings ++ for (auto it = peonySettings.begin(); it != peonySettings.end(); ++it) { ++ const QString internalKey = convertJsonKeyToInternalKey(it.key()); ++ const QVariant newValue = it.value().toVariant(); ++ newKeys.insert(internalKey); ++ ++ // Check if value has changed ++ auto existingValue = m_jsonCache.find(internalKey); ++ if (existingValue == m_jsonCache.end() || existingValue.value() != newValue) { ++ m_jsonCache[internalKey] = newValue; ++ Q_EMIT this->valueChanged(internalKey); ++ } ++ } ++ ++ // Find and remove cached items that no longer exist in the configuration file ++ QList<QString> keysToRemove; ++ for (auto it = m_jsonCache.begin(); it != m_jsonCache.end(); ++it) { ++ if (!newKeys.contains(it.key())) { ++ keysToRemove.append(it.key()); ++ } ++ } ++ ++ // Remove obsolete items and emit signals ++ for (const QString &keyToRemove : keysToRemove) { ++ m_jsonCache.remove(keyToRemove); ++ Q_EMIT this->valueChanged(keyToRemove); ++ } ++} ++ ++QString GlobalSettings::constructConfigPath(const QString &username) const ++{ ++ return QString("%1/%2/%3.json").arg(CONFIG_BASE_PATH, ++ username, ++ PEONY_CONFIG_NAME); ++} ++ ++bool GlobalSettings::isDesktopStartUp() const ++{ ++ return m_isDesktopStartUp; ++} ++ ++void GlobalSettings::setDesktopStartUp(bool startUp) ++{ ++ m_isDesktopStartUp = startUp; ++} ++ + const QVariant GlobalSettings::getValue(const QString &key) + { ++ // First try to get value from JSON cache ++ auto jsonIt = m_jsonCache.find(key); ++ if (jsonIt != m_jsonCache.end()) { ++ return jsonIt.value(); ++ } ++ ++ // Fallback to GSettings cache + return m_cache.value(key); + } + +@@ -455,6 +657,31 @@ bool GlobalSettings::isExist(const QString &key) + return !m_cache.value(key).isNull(); + } + ++void GlobalSettings::sendNotifyMessage(const QString &msg) ++{ ++ if (! QDBusConnection::sessionBus().isConnected()) ++ return; ++ ++ QDBusInterface iface ("org.freedesktop.Notifications", ++ "/org/freedesktop/Notifications", ++ "org.freedesktop.Notifications", QDBusConnection::sessionBus ()); ++ ++ QList <QVariant> args; ++ QStringList actions; ++ QMap <QString, QVariant> hints; ++ ++ args << QObject::tr("File Manager").toUtf8().constData() ++ << ((unsigned int) 0) ++ << "system-file-manager" ++ << QObject::tr("Notify") ++ << msg ++ << actions ++ << hints ++ << (int) -1; ++ ++ iface.callWithArgumentList (QDBus::AutoDetect, "Notify", args); ++} ++ + void GlobalSettings::reset(const QString &key) + { + m_cache.remove(key); +@@ -517,10 +744,11 @@ void GlobalSettings::forceSync(const QString &key) + } + } else { + m_cache.remove(key); +- if (m_settings->allKeys().contains(key)) { +- m_cache.insert(key, m_settings->value(key)); +- } else if (m_peony_gsettings->keys().contains(key)) { ++ // QGSettings takes priority over QSSettings ++ if (m_peony_gsettings->keys().contains(key)) { + m_cache.insert(key, m_peony_gsettings ? m_peony_gsettings->get(key) : QVariant()); ++ } else if (m_settings->allKeys().contains(key)) { ++ m_cache.insert(key, m_settings->value(key)); + } else { + qWarning()<<"key"<<key<<"doesn't exsit either qsettings and gsettings"; + } +@@ -537,6 +765,24 @@ bool GlobalSettings::isGuestOSMachine() + return m_cache.value(IS_GUESTOS_MACHINE).toBool(); + } + ++void GlobalSettings::onConfigFileChanged(const QString &username, const QString &configName, const QString &configPath) ++{ ++ // Only process if it's our config ++ if (configName != PEONY_CONFIG_NAME) { ++ return; ++ } ++ ++ // Verify the config path ++ QString expectedPath = constructConfigPath(username); ++ if (configPath != expectedPath) { ++ qWarning() << "Unexpected config path:" << configPath; ++ return; ++ } ++ ++ m_currentConfigPath = configPath; ++ loadJsonConfig(configPath); ++} ++ + void GlobalSettings::setTimeFormat(const QString &value) + { + if (value == "12"){ +diff --git a/libpeony-qt/global-settings.h b/libpeony-qt/global-settings.h +index 22854f1..4653016 100644 +--- a/libpeony-qt/global-settings.h ++++ b/libpeony-qt/global-settings.h +@@ -27,6 +27,7 @@ + #include <QSettings> + #include <QMutex> + #include <QDBusInterface> ++#include <QJsonObject> + + #include "peony-core_global.h" + #include <gio/gio.h> +@@ -37,10 +38,11 @@ + //顶部菜单 - Top menu + #define RESIDENT_IN_BACKEND "resident" + #define SHOW_HIDDEN_PREFERENCE "showHiddenFile" +-#define ALLOW_FILE_OP_PARALLEL "allow-file-op-parallel" ++#define ALLOW_FILE_OP_PARALLEL "allowFileOpParallel" + #define FORBID_THUMBNAIL_IN_VIEW "doNotThumbnail" + #define SHOW_IN_NEW_WINDOW "showInNewWindow" + #define DISABLED_EXTENSIONS "disabledExtensions" ++#define SHOW_SHARE_PROPERTIES "showShareProperties" + + //视图 - View + #define DEFAULT_VIEW_ID "defaultViewId" +@@ -81,6 +83,7 @@ + + //桌面配置 - Desktop setting + #define LAST_DESKTOP_SORT_ORDER "lastDesktopSortOrder" ++#define DESKTOP_SORT_ORDER "desktop-sort-order" + #define TEMPLATES_DIR "templatesDir" + #define DEFAULT_DESKTOP_ZOOM_LEVEL "defaultDesktopZoomLevel" + #define DEFAULT_GRID_SIZE "default-grid-size" +@@ -115,11 +118,26 @@ + //control the mobile device trash file issue, if be true can trash mobile files + #define TRASH_MOBILE_FILES "trashMobileFiles" + ++//control the display of right menu terminal ++#define SHOW_OPEN_TERMINAL "showOpenTerminal" ++ ++//Control start peony ++#define ENABLE_START_PEONY "enableStartPeony" ++ ++//Control double click desktop file ++#define ENABLE_DOUBLE_CLICK_DESKTOP "enableDoubleClickDesktop" ++ ++//Control file operation of shortcut keys, such as Ctrl+C、Ctrl+V、Ctrl+A、Delete、Shift+Delete、Ctrl+X ++#define ENABLE_SHORTCUT_KEYS "enableShortcutKeys" ++ + // control center + #define UKUI_CONTROL_CENTER_PANEL_PLUGIN "org.ukui.control-center.panel.plugins" // schema + #define UKUI_CONTROL_CENTER_PANEL_PLUGIN_TIME "org.ukui.control-center.panel.plugins.time" // time format key, value is '12' or '24' + #define UKUI_CONTROL_CENTER_PANEL_PLUGIN_DATE "org.ukui.control-center.panel.plugins.date" // date format key, value is cn or en + ++// desktop ++#define DESKTOP_USE_AUTO_LAYOUT "desktopUseAutoLayout" ++ + // guestos machine + #define IS_GUESTOS_MACHINE "isGuestOSMachine" + +@@ -153,6 +171,13 @@ + #define SDK_DATE_SERVER_INTERFACE "com.kylin.kysdk.DateInterface" + #endif + ++// Configuration paths and schema ++#define CONFIG_BASE_PATH "/usr/share/ukui-config" ++#define PEONY_SCHEMA_ID "org.ukui.peony.settings" ++#define PEONY_SCHEMA_PATH "/org/ukui/peony/settings/" ++#define PEONY_CONFIG_NAME "org.ukui.peony.settings" ++#define PEONY_SETTINGS_KEY "org.ukui.peony.settings" ++ + class QGSettings; + + namespace Peony { +@@ -176,6 +201,9 @@ public: + bool isExist(const QString &key); + bool initDateFormatDBus(); + QString getProjectName(); ++ bool isDesktopStartUp() const; ++ void setDesktopStartUp(bool startUp);/* 桌面启动和结束时使用,谨慎调用 */ ++ void sendNotifyMessage(const QString &msg); + + bool getShowCreateTime() const; + +@@ -216,6 +244,15 @@ public Q_SLOTS: + + bool isGuestOSMachine(); + ++ /** ++ * @brief Slot for handling configuration file changes ++ * @param username The username ++ * @param configName The configuration name ++ * @param configPath The configuration file path ++ */ ++ void onConfigFileChanged(const QString &username, const QString &configName, ++ const QString &configPath); ++ + private: + explicit GlobalSettings(QObject *parent = nullptr); + ~GlobalSettings(); +@@ -223,6 +260,63 @@ private: + void getUkuiStyle(); + void getMachineMode(); + void getDualScreenMode(); ++ /** ++ * @brief Initialize safe manage control configuration sources ++ */ ++ void initManageControl(); ++ ++ /** ++ * @brief Load and parse JSON configuration file ++ * ++ * This function attempts to load and parse a JSON configuration file from the specified path. ++ * If the file cannot be opened, parsed, or contains invalid data, the JSON cache will be cleared. ++ * ++ * The JSON structure should follow the format: ++ * { ++ * "org.ukui.peony.settings": { ++ * "setting-key1": "value1", ++ * "setting-key2": "value2" ++ * } ++ * } ++ * ++ * @param configPath The full path to the JSON configuration file ++ * @return bool Returns true if the configuration was successfully loaded and parsed, ++ * false if any error occurred during the process ++ * ++ * @note On any failure (file not found, parse error, empty content), ++ * the internal JSON cache (m_jsonCache) will be cleared ++ * ++ * @see updateConfigCache() ++ */ ++ bool loadJsonConfig(const QString &configPath); ++ ++ /** ++ * @brief Initialize DBus connection ++ */ ++ void initDBus(); ++ ++ /** ++ * @brief Convert JSON configuration key to internal key format ++ * @param jsonKey The key in JSON format ++ * @return QString The converted internal key ++ */ ++ QString convertJsonKeyToInternalKey(const QString &jsonKey); ++ ++ /** ++ * @brief Update configuration cache and emit signals for changed values ++ * @param newConfig The new configuration object ++ */ ++ void updateConfigCache(const QJsonObject &peonySettings); ++ ++ /** ++ * @brief Construct configuration file path ++ * @param username The username ++ * @return QString The complete configuration file path ++ */ ++ QString constructConfigPath(const QString &username) const; ++ ++ QMap<QString, QVariant> m_jsonCache; ///< Cache for JSON configuration values ++ QString m_currentConfigPath; + + QSettings* m_settings; + QMap<QString, QVariant> m_cache; +@@ -242,6 +336,7 @@ private: + QDBusInterface* mDbusDateServer = nullptr; + bool m_showCreateTime = false; + bool m_showRelativeTime = false; ++ bool m_isDesktopStartUp = false; /* 桌面进程启动中 */ + }; + + } +diff --git a/libpeony-qt/libpeony-qt.pro b/libpeony-qt/libpeony-qt.pro +index 98a0b4e..f16c8ca 100644 +--- a/libpeony-qt/libpeony-qt.pro ++++ b/libpeony-qt/libpeony-qt.pro +@@ -95,7 +95,8 @@ unix { + # fixme:// format_dialog.h + header.path = /usr/include/peony-qt + header.files += *.h model/*.h file-operation/*.h vfs/*.h controls/ ../plugin-iface/*.h convenient-utils/*.h convenient-utils/disc/*.h windows/format_dialog.h windows/FMWindowIface.h \ +- libpeony-qt/usershare-manager.h windows/udfFormatDialog.h windows/udfAppendBurnDataDialog.h windows/format-dlg-create-delegate.h file-launcher/*.h private/*.h ++ libpeony-qt/usershare-manager.h windows/udfFormatDialog.h windows/udfAppendBurnDataDialog.h windows/format-dlg-create-delegate.h file-launcher/*.h private/*.h windows/properties-window.h ++ + # header.depends = header2 + header.files += development-files/header-files/* + INSTALLS += header +diff --git a/libpeony-qt/model/file-item-model.cpp b/libpeony-qt/model/file-item-model.cpp +index 404fde1..e69b022 100644 +--- a/libpeony-qt/model/file-item-model.cpp ++++ b/libpeony-qt/model/file-item-model.cpp +@@ -38,13 +38,15 @@ + #include "global-settings.h" + + #include "file-operation-utils.h" +- ++#include "directory-view-container.h" + #include "emblem-provider.h" + #include "sound-effect.h" + #ifdef KY_SDK_SOUND_EFFECTS + #include "ksoundeffects.h" + #endif + ++#include "tooltips-manager.h" ++ + #include <QIcon> + #include <QMimeData> + #include <QUrl> +@@ -93,14 +95,17 @@ FileItemModel::~FileItemModel() + { + qDebug()<<"~FileItemModel"; + disconnect(); +- if (m_root_item) ++ if (m_root_item){ + delete m_root_item; ++ m_root_item = nullptr; ++ } + + if(m_fileManagerThread){ + m_fileManagerThread->quit(); + m_fileManagerThread->wait(); + // 无法使用deleteLater()删除,因为此线程已经被移入自身,而且已经退出无法处理事件 + delete m_fileManagerThread; ++ m_fileManagerThread = nullptr; + } + } + +@@ -127,9 +132,16 @@ void FileItemModel::setRootUri(const QString &uri) + void FileItemModel::setRootItem(FileItem *item) + { + beginResetModel(); +- m_root_item->deleteLater(); + ++ /* 解决使用'm_root_item->deleteLater();'时光盘弹出切换到计算机视图后,偶现旧数据没有析构的问题;link bug#208641 向UDF格式R盘拖拽添加文件,文管内文件图标显示错乱。修改于2024-8-13 ++ * 解决使用'delete m_root_item;'时双击侧边栏远程服务,第二次m_root_item未被赋值新item,未挂载路径回退时导致二次析构;link bug#267007 双击共享文件夹链接地址文件管理闪退。修改于2024-9-25 */ ++ FileItem *oldData = m_root_item; + m_root_item = item; ++ if(oldData){ ++ delete oldData; ++ oldData = nullptr; ++ } ++ //end hotfix bug#208641、267007 + + m_root_item->connectFunc(); + if(m_infosJob){ +@@ -237,7 +249,7 @@ QModelIndex FileItemModel::parent(const QModelIndex &child) const + int FileItemModel::columnCount(const QModelIndex &parent) const + { + Q_UNUSED(parent); +- if (m_root_uri == "trash:///") { ++ if (m_root_uri == "trash:///" || m_root_uri.startsWith("search:///")) { + return FileSize + 2; + } + return FileSize+1; +@@ -278,6 +290,12 @@ QVariant FileItemModel::data(const QModelIndex &index, int role) const + case Qt::DisplayRole: { + //fix bug#53504, desktop files not show same name issue + QString displayName = item->m_info->displayName(); ++ ++ if ("computer:///root.link" == item->m_info->uri()) { ++ displayName = tr("System Disk"); ++ return QVariant(displayName); ++ } ++ + if (item->m_info->isDesktopFile()) + { + displayName = FileUtils::handleDesktopFileName(item->m_info->uri(), item->m_info->displayName()); +@@ -293,21 +311,20 @@ QVariant FileItemModel::data(const QModelIndex &index, int role) const + return QVariant(displayName); + } + case Qt::DecorationRole: { +- auto thumbnail = ThumbnailManager::getInstance()->tryGetThumbnail(item->m_info->uri()); +- if (!thumbnail.isNull()) { +- return thumbnail; ++ if (!item->m_info->isExistTargetOfSymlink()) { ++ return QIcon::fromTheme("unknown"); + } +- QIcon icon = QIcon::fromTheme(item->m_info->iconName(), QIcon::fromTheme("unknown")); +- return QVariant(icon); ++ return item->m_info->getIcon(); + } + case Qt::ToolTipRole: { +- //fix bug#53504, desktop files not show same name issue +- if (item->m_info->isDesktopFile()) +- { +- auto displayName = FileUtils::handleDesktopFileName(item->m_info->uri(), item->m_info->displayName()); +- return QVariant(displayName); +- } +- return QVariant(item->m_info->displayName()); ++ /** ++ * @task #346285: 【Tooltips specification】Change the desktop icon (including the management-desktop-application icon) tooltips display, ++ * add supplementary text ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-09-25 ++ */ ++ return QVariant(TooltipsManagerInstance.generateTooltip(item->m_info.get())); + } + case Qt::UserRole + 1: { + return item->m_info->displayName(); +@@ -364,22 +381,34 @@ QVariant FileItemModel::data(const QModelIndex &index, int role) const + return QVariant(); + } + } +- case TrashOriginPath: { ++ case FilePath: { + switch (role) { + case Qt::DisplayRole: + case Qt::ToolTipRole: { +- QString originPath = item->m_info->property("orig-path").toString(); +- if (originPath.isEmpty()) { +- auto targetInfo = FileInfo::fromUri(item->m_info->targetUri()); +- if (targetInfo->isEmptyInfo()) { +- FileInfoJob j(targetInfo); +- j.querySync(); +- originPath = FileMetaInfo::fromUri(targetInfo->uri())->getMetaInfoString("orig-path"); +- item->m_info->setProperty("orig-path", originPath); ++ if(m_root_uri.startsWith("trash://")){ ++ QString originPath = item->m_info->property("orig-path").toString(); ++ if (originPath.isEmpty()) { ++ auto targetInfo = FileInfo::fromUri(item->m_info->targetUri()); ++ if (targetInfo->isEmptyInfo()) { ++ FileInfoJob j(targetInfo); ++ j.querySync(); ++ originPath = FileMetaInfo::fromUri(targetInfo->uri())->getMetaInfoString("orig-path"); ++ item->m_info->setProperty("orig-path", originPath); ++ } ++ return originPath; + } + return originPath; ++ }else if(m_root_uri.startsWith("search:///")){ ++ QString path = item->m_info->filePath(); ++ if(!path.isEmpty()){ ++ return path; ++ }else if(item->uri().startsWith("trash://")){ ++ return item->m_info->property("orig-path").toString(); ++ }else{ ++ QString targetUri = item->m_info.get()->targetUri(); ++ return QUrl(targetUri).path(); ++ } + } +- return originPath; + break; + } + default: +@@ -414,8 +443,12 @@ QVariant FileItemModel::headerData(int section, Qt::Orientation orientation, int + return tr("File Type"); + case FileSize: + return tr("File Size"); +- case TrashOriginPath: +- return tr("Original Path"); ++ case FilePath:{ ++ if (m_root_uri.startsWith("trash:///")) ++ return tr("Original Path"); ++ if(m_root_uri.startsWith("search:///")) ++ return tr("Path"); ++ } + + default: + return QVariant(); +@@ -455,10 +488,13 @@ Qt::ItemFlags FileItemModel::flags(const QModelIndex &index) const + return flags; + } else { + if (m_root_item) { ++ QString fsType = m_root_item->m_info.get()->fileSystemType(); + if (m_root_item->m_info->canWrite()) { + return Qt::ItemIsDropEnabled; +- } else if(m_root_item->m_info.get()->fileSystemType().contains("udf")) { ++ } else if(fsType.contains("udf") || fsType.isEmpty()) { + return Qt::ItemIsDropEnabled; ++ } else { ++ return Qt::ItemIsEnabled; + } + } + return Qt::ItemIsDropEnabled; +@@ -794,6 +830,28 @@ void FileItemModel::setShowFileExtensions(bool show) + GlobalSettings::getInstance()->setGSettingValue(SHOW_FILE_EXTENSION, show); + } + ++void FileItemModel::insetFileInfoData(std::vector<std::shared_ptr<FileInfo> > &fileInfoVec, FileItem *parentItem) ++{ ++ if(!parentItem || 0 == fileInfoVec.size()) ++ return; ++ ++ FileItem *oldData = m_root_item; ++ m_root_item = parentItem; ++ if(oldData){ ++ delete oldData; ++ oldData = nullptr; ++ } ++ m_root_uri = m_root_item->uri(); ++ ++ beginInsertRows(QModelIndex(), 0, fileInfoVec.size() -1); ++ for (auto& info : fileInfoVec) { ++ FileItem *item = new FileItem(info, m_root_item, this); ++ m_root_item->m_children->append(item); ++ m_root_item->m_uri_item_hash.insert(item->uri(), item); ++ } ++ endInsertRows(); ++} ++ + const QModelIndex FileItemModel::indexFromItemAndUri(FileItem *item, const QString &uri) + { + if(!item) +diff --git a/libpeony-qt/model/file-item-model.h b/libpeony-qt/model/file-item-model.h +index c01eff3..a613050 100644 +--- a/libpeony-qt/model/file-item-model.h ++++ b/libpeony-qt/model/file-item-model.h +@@ -63,7 +63,7 @@ public: + ModifiedDate, + FileType, + FileSize, +- TrashOriginPath, ++ FilePath, + Owner, + Other + }; +@@ -225,6 +225,8 @@ public: + + void setShowFileExtensions(bool show); + ++ void insetFileInfoData(std::vector<std::shared_ptr<FileInfo> >& fileInfoVec, FileItem *parentItem); ++ + private: + const QModelIndex indexFromItemAndUri(FileItem *item, const QString &uri); + +@@ -256,7 +258,9 @@ Q_SIGNALS: + * \note proxy model should connect this signal and start sort and filter again. + */ + void updated(); +- ++ void updateFilter(); ++ void signal_updateTabPageTitle(const QString& uri); ++ void signal_updateLocationBar(const QString& uri); + void selectRequest(const QStringList &uris); + void changePathRequest(const QString &destUri, const QString &sourceUri); + +diff --git a/libpeony-qt/model/file-item-proxy-filter-sort-model.cpp b/libpeony-qt/model/file-item-proxy-filter-sort-model.cpp +index 8096a68..8137ea5 100644 +--- a/libpeony-qt/model/file-item-proxy-filter-sort-model.cpp ++++ b/libpeony-qt/model/file-item-proxy-filter-sort-model.cpp +@@ -55,7 +55,7 @@ const QString getModelDirectoryUri(FileItemProxyFilterSortModel *model) + { + FileItemModel *srcModel = qobject_cast<FileItemModel *>(model->sourceModel()); + if (!srcModel) { +- qInfo()<<"source model not available now"; ++ qInfo()<<"source model not avaliable now"; + return nullptr; + } + return srcModel->getRootUri(); +@@ -89,7 +89,8 @@ FileItemProxyFilterSortModel::FileItemProxyFilterSortModel(QObject *parent) : QS + connect(m_sortTimer, &QTimer::timeout, this, [=]{ + checkSortSettings(); + qDebug()<<"sort type:"<<m_sortType<<" sort order:"<<m_sortOrder<<" folder first:"<<m_folder_first; +- return QSortFilterProxyModel::sort(m_sortType, m_sortOrder); ++ QSortFilterProxyModel::sort(m_sortType, m_sortOrder); ++ Q_EMIT this->sortFinished(); + }); + + m_show_hidden = settings->isExist(SHOW_HIDDEN_PREFERENCE)? settings->getValue(SHOW_HIDDEN_PREFERENCE).toBool(): false; +@@ -267,9 +268,16 @@ bool FileItemProxyFilterSortModel::lessThan(const QModelIndex &left, const QMode + } + return leftItem->m_info->modifiedTime() > rightItem->m_info->modifiedTime(); + } +- case FileItemModel::TrashOriginPath: { +- auto leftString = leftItem->m_info->property("orig-path").toString(); +- auto rightString = rightItem->m_info->property("orig-path").toString(); ++ case FileItemModel::FilePath: { ++ QString leftString,rightString; ++ if(leftItem->uri().startsWith("trash://")){ ++ leftString= leftItem->m_info->property("orig-path").toString(); ++ rightString = rightItem->m_info->property("orig-path").toString(); ++ } ++ if(leftItem->uri().startsWith("search:///")){ ++ leftString = leftItem->m_info->filePath(); ++ rightString = rightItem->m_info->filePath(); ++ } + if (leftString == rightString) { + goto default_sort; + } +@@ -323,27 +331,18 @@ bool FileItemProxyFilterSortModel::filterAcceptsRow(int sourceRow, const QModelI + auto childIndex = model->index(sourceRow, 0, sourceParent); + if (childIndex.isValid()) { + auto item = static_cast<FileItem*>(childIndex.internalPointer()); +- +- /* task#63345 通过.hidden文件来设置隐藏文件和目录 */ + FileInfo* fileInfo = item->m_info.get();/*FileInfo::fromUri(item->uri()).get()*/; ++ //use same way with v101-latest code, fix set more than one file hide has no effect issue + bool isHidden = fileInfo->property(G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN).toBool(); + //qDebug()<<"File view .hidden file hidden,uri:"<<item->uri()<<" isHidden:"<<isHidden; +- if(isHidden && !fileInfo->displayName().startsWith(".")){/* .xxx文件遵循是否显示隐藏文件的逻辑 */ ++ if(!m_show_hidden && isHidden){ + return false; + }//end + + if(!item->shouldShow()) + return false; +- if (!m_show_hidden) { +- //qDebug()<<sourceRow<<item->m_info->displayName()<<model->rowCount(sourceParent); +- //QMessageBox::warning(nullptr, "filter", item->m_info->displayName()); +- //qDebug()<<item->m_info->displayName(); +- if (item->m_info->displayName() != nullptr) { +- if (item->m_info->displayName().at(0) == '.') { +- //qDebug()<<sourceRow<<item->m_info->displayName()<<model->rowCount(sourceParent); +- return false; +- } +- } ++ if (!m_show_hidden && item->m_info->isHiddenFile()) { ++ return false; + } + //regExp + +@@ -368,6 +367,10 @@ bool FileItemProxyFilterSortModel::filterAcceptsRow(int sourceRow, const QModelI + if (item->m_info->displayName().contains("839 MB")) + return false; + ++ if (model->getRootUri().startsWith("search://")) { ++ calculationAll(item); ++ } ++ + //check the file info filter conditions + //qDebug()<<"start filter conditions check"<<item->m_info->displayName()<<item->m_info->type(); + if (! checkFileTypeFilter(item->m_info->type())) +@@ -390,6 +393,10 @@ bool FileItemProxyFilterSortModel::filterAcceptsRow(int sourceRow, const QModelI + else if (! checkFileNameFilter(item->m_info->displayName())) + return false; + ++ if (!checkFileContentFilter(item->m_info->displayName())) { ++ return false; ++ } ++ + //check the file label filter conditions + if (m_label_name != "" || m_label_color != Qt::transparent) + { +@@ -410,11 +417,11 @@ bool FileItemProxyFilterSortModel::filterAcceptsRow(int sourceRow, const QModelI + } + + //check multiple label filter conditions, file has any one of these label is accepted +- if(m_show_label_names.size() >0 || m_show_label_colors.size() >0) ++ if(m_show_label_names.size() > 0 || m_show_label_colors.size() > 0) + { + bool bfind = false; + QString uri = item->m_info->uri(); +- if (m_show_label_names.size() >0 ) ++ if (m_show_label_names.size() > 0) + { + auto names = FileLabelModel::getGlobalModel()->getFileLabels(uri); + for(auto temp : m_show_label_names) +@@ -427,7 +434,7 @@ bool FileItemProxyFilterSortModel::filterAcceptsRow(int sourceRow, const QModelI + } + } + +- if (! bfind && m_show_label_colors.size() >0) ++ if (! bfind && m_show_label_colors.size() > 0) + { + auto colors = FileLabelModel::getGlobalModel()->getFileColors(uri); + for(auto temp : m_show_label_colors) +@@ -530,6 +537,18 @@ bool FileItemProxyFilterSortModel::checkFileNameFilter(const QString &displayNam + return false; + } + ++bool FileItemProxyFilterSortModel::checkFileContentFilter(const QString &displayName) const ++{ ++ if (m_fileContent.isEmpty()) ++ return true; ++ ++ if (displayName.contains(m_fileContent, Qt::CaseInsensitive)) { ++ return true; ++ } ++ ++ return false; ++} ++ + void FileItemProxyFilterSortModel::checkSortSettings() + { + m_use_global_sort = m_settings->getValue(USE_GLOBAL_DEFAULT_SORTING).toBool(); +@@ -606,6 +625,109 @@ void FileItemProxyFilterSortModel::setDirectorySettings(const QString &key, cons + } + } + ++void FileItemProxyFilterSortModel::calculationFileTypeCount(QString type) const ++{ ++ int fileType = ALL_TYPE; ++ if (type == Folder_Type) { ++ fileType = FILE_FOLDER; ++ } else if (type.contains(Image_Type)) { ++ fileType = PICTURE; ++ } else if (type.contains(Video_Type)) { ++ fileType = VIDEO; ++ } else if (type.contains(Text_Type)) { ++ fileType = TXT_FILE; ++ } else if (type.contains(Wps_Type)) { ++ fileType = WPS_FILE; ++ } else if (type.contains(Audio_Type) || type.contains("application/x-smaf")) { ++ fileType = AUDIO; ++ } else if (type != Folder_Type && ! type.contains(Image_Type) && ! type.contains(Video_Type) ++ && ! type.contains(Text_Type) && !type.contains(Wps_Type) && ! type.contains(Audio_Type) && !type.contains("application/x-smaf")) { ++ fileType = OTHERS; ++ } ++ ++ m_file_type_map[fileType]++; ++} ++ ++void FileItemProxyFilterSortModel::calculationFileModifyTimeCount(quint64 modifiedTime) const ++{ ++ auto time = QDateTime::currentMSecsSinceEpoch(); ++ auto dateTime = QDateTime::fromMSecsSinceEpoch(time); ++ QDate date = dateTime.date(); ++ int year = date.year(); ++ int month = date.month(); ++ int day = date.day(); ++ QDate md_date = QDateTime::fromMSecsSinceEpoch(modifiedTime * 1000).date(); ++ int md_year= md_date.year(); ++ int md_month = md_date.month(); ++ int md_day = md_date.day(); ++ ++ int lastMonth = (month == 1) ? 12 : month - 1; ++ ++ int fileModifyTime = ALL_TIME; ++ if (year == md_year && month == md_month && day == md_day) { ++ fileModifyTime = TODAY; ++ } else if (date.addDays(-1) == md_date) { ++ fileModifyTime = YESTERDAY; ++ } else if (date.weekNumber() == md_date.weekNumber() && year == md_year) { ++ fileModifyTime = THIS_WEEK; ++ } else if (date.addDays(-date.dayOfWeek() + 1).addDays(-7) <= md_date && md_date <= date.addDays(-date.dayOfWeek())) { ++ fileModifyTime = LAST_WEEK; ++ } else if (year == md_year && month == md_month) { ++ fileModifyTime = THIS_MONTH; ++ } else if (md_month == lastMonth) { ++ if ((month == 1 && md_year == (year - 1)) || (month !=1 && year == md_year)) { ++ fileModifyTime = LAST_MONTH; ++ } ++ } else if (year == md_year) { ++ fileModifyTime = THIS_YEAR; ++ } else if (md_year == year - 1) { ++ fileModifyTime = LAST_YEAR; ++ } ++ ++ m_file_modify_time_map[fileModifyTime]++; ++} ++ ++void FileItemProxyFilterSortModel::calculationFileSizeCount(quint64 size) const ++{ ++ int fileSize = ALL_SIZE; ++ if (size == 0) { ++ fileSize = EMPTY; ++ } else if (size > 0 && size <= TINY_BASE) { ++ fileSize = TINY; ++ } else if (size > TINY_BASE && size <= SMALL_BASE) { ++ fileSize = SMALL; ++ } else if (size > SMALL_BASE && size <= MEDIUM_BASE) { ++ fileSize = MEDIUM; ++ } else if (size > MEDIUM_BASE && size <= BIG_BASE) { ++ fileSize = BIG; ++ } else if (size > BIG_BASE && size <= LARGE_BASE) { ++ fileSize = LARGE; ++ } else if (size > LARGE_BASE) { ++ fileSize = GREAT; ++ } ++ ++ m_file_size_map[fileSize]++; ++} ++ ++void FileItemProxyFilterSortModel::calculationFileLabelCount(QStringList names, QList<QColor> colors) const ++{ ++ QList<FileLabelItem *> allLabels = FileLabelModel::getGlobalModel()->getAllFileLabelItems(); ++ int size = allLabels.size(); ++ for (int i = 0; i < size; i++) { ++ if (names.contains(allLabels.at(i)->name()) && colors.contains(allLabels.at(i)->color())) { ++ m_file_label_map[i]++; ++ } ++ } ++} ++ ++void FileItemProxyFilterSortModel::calculationAll(FileItem *item) const ++{ ++ calculationFileTypeCount(item->m_info->type()); ++ calculationFileModifyTimeCount(item->m_info->modifiedTime()); ++ calculationFileSizeCount(item->m_info->size()); ++ calculationFileLabelCount(FileLabelModel::getGlobalModel()->getFileLabels(item->m_info->uri()), FileLabelModel::getGlobalModel()->getFileColors(item->m_info->uri())); ++} ++ + bool FileItemProxyFilterSortModel::checkFileTypeFilter(QString type) const + { + //qDebug()<<"m_show_file_type: "<<m_show_file_type<<" "<<type; +@@ -621,7 +743,8 @@ bool FileItemProxyFilterSortModel::checkFileTypeFilter(QString type) const + if (! totalTypeList.contains(m_show_file_type) && m_show_file_type != ALL_FILE) + totalTypeList.append(m_show_file_type); + +- for(int i=0; i<totalTypeList.count(); i++) ++ int count = totalTypeList.count(); ++ for(int i = 0; i < count; i++) + { + auto cur = totalTypeList[i]; + switch (cur) +@@ -698,7 +821,8 @@ bool FileItemProxyFilterSortModel::checkFileModifyTimeFilter(quint64 modifiedTim + int md_month = md_date.month(); + int md_day = md_date.day(); + +- for(int i=0; i<m_modify_time_list.size(); i++) ++ int count = m_modify_time_list.size(); ++ for(int i = 0; i < count; i++) + { + auto cur = m_modify_time_list[i]; + switch(cur) +@@ -729,7 +853,7 @@ bool FileItemProxyFilterSortModel::checkFileModifyTimeFilter(quint64 modifiedTim + case LAST_WEEK: + { + QDate monDate = date.addDays(-date.dayOfWeek() + 1); +- if(monDate.addDays(-7)<= md_date && md_date <= monDate.addDays(-1))/* 判断给定日期是否在上周 */ ++ if(monDate.addDays(-7) <= md_date && md_date <= monDate.addDays(-1))/* 判断给定日期是否在上周 */ + return true; + break; + } +@@ -777,50 +901,51 @@ bool FileItemProxyFilterSortModel::checkFileSizeFilter(quint64 size) const + if (m_file_size_list.count() == 0 || m_file_size_list.contains(ALL_FILE)) + return true; + +- for(int i=0; i<m_file_size_list.count(); i++) ++ int count = m_file_size_list.count(); ++ for(int i = 0; i < count; i++) + { + auto cur = m_file_size_list[i]; + switch (cur) + { + case EMPTY: //[0K] + { +- if (size ==0 ) ++ if (size == 0) + return true; + break; + } + case TINY: //(0-16K] + { +- if (size > 0 &&size <=16 * K_BASE) ++ if (size > 0 && size <= TINY_BASE) + return true; + break; + } + case SMALL: //(16k-1M] + { +- if(size > 16 * K_BASE && size <=K_BASE * K_BASE) ++ if(size > TINY_BASE && size <= SMALL_BASE) + return true; + break; + } + case MEDIUM: //(1M-128M] + { +- if(size > K_BASE * K_BASE && size <= 128 * K_BASE * K_BASE) ++ if(size > SMALL_BASE && size <= MEDIUM_BASE) + return true; + break; + } + case BIG: //(128M-1G] + { +- if(size > 128 * K_BASE * K_BASE && size <= K_BASE * K_BASE * K_BASE) ++ if(size > MEDIUM_BASE && size <= BIG_BASE) + return true; + break; + } + case LARGE: //(1-4G] + { +- if (size > K_BASE * K_BASE * K_BASE&& size <= 4*K_BASE * K_BASE * K_BASE) ++ if (size > BIG_BASE && size <= LARGE_BASE) + return true; + break; + } + case GREAT: //>4G + { +- if (size > 4*K_BASE * K_BASE * K_BASE) ++ if (size > LARGE_BASE) + return true; + break; + } +@@ -959,6 +1084,26 @@ void FileItemProxyFilterSortModel::clearConditions() + m_mimeTypeFilters.clear(); + m_nameFilters.clear(); + m_dirFilters = -1; ++ ++ m_show_label_names.clear(); ++ m_show_label_colors.clear(); ++ m_file_type_map.clear(); ++ m_file_modify_time_map.clear(); ++ m_file_size_map.clear(); ++ m_file_label_map.clear(); ++} ++ ++void FileItemProxyFilterSortModel::addFileContentFilter(QString key, bool updateNow) ++{ ++ qDebug() << __func__ << key; ++ m_fileContent = key; ++ if (updateNow) ++ invalidateFilter(); ++} ++ ++void FileItemProxyFilterSortModel::clearFileContentConditions() ++{ ++ m_fileContent.clear(); + } + + void FileItemProxyFilterSortModel::setFilterConditions(int fileType, int modifyTime, int fileSize) +@@ -1076,6 +1221,55 @@ void FileItemProxyFilterSortModel::manualUpdateExpectedSortInfo(int sortType, Qt + m_sortOrder = order; + } + ++void FileItemProxyFilterSortModel::clearAllMapsCount() ++{ ++ m_file_type_map.clear(); ++ m_file_modify_time_map.clear(); ++ m_file_size_map.clear(); ++ m_file_label_map.clear(); ++} ++ ++QMap<int, int> FileItemProxyFilterSortModel::getFileTypeCount() ++{ ++ return m_file_type_map; ++} ++ ++QMap<int, int> FileItemProxyFilterSortModel::getFileModifyTimeCount() ++{ ++ return m_file_modify_time_map; ++} ++ ++QMap<int, int> FileItemProxyFilterSortModel::getFileSizeCount() ++{ ++ return m_file_size_map; ++} ++ ++QMap<int, int> FileItemProxyFilterSortModel::getFileLabelCount() ++{ ++ return m_file_label_map; ++} ++ ++void FileItemProxyFilterSortModel::checkSettingsAndSort() ++{ ++ checkSortSettings(); ++ invalidateFilter(); ++ ++ auto settings = Peony::GlobalSettings::getInstance(); ++ if (settings->getValue(USE_GLOBAL_DEFAULT_SORTING).toBool()) { ++ m_sortType = settings->isExist(SORT_COLUMN)? settings->getValue(SORT_COLUMN).toInt(): 0; ++ m_sortOrder = Qt::SortOrder(settings->isExist(SORT_ORDER)? settings->getValue(SORT_ORDER).toInt(): 1); ++ } else { ++ auto info = FileInfo::fromUri(getModelDirectoryUri(this)); ++ auto fileMetaInfo = FileMetaInfo::fromUri(getModelDirectoryUri(this)); ++ if (fileMetaInfo && !info->isEmptyInfo()) { ++ m_sortType = fileMetaInfo->getMetaInfoVariant(SORT_COLUMN).isValid()? fileMetaInfo->getMetaInfoInt(SORT_COLUMN): 0; ++ m_sortOrder = Qt::SortOrder(fileMetaInfo->getMetaInfoVariant(SORT_ORDER).isValid()? fileMetaInfo->getMetaInfoInt(SORT_ORDER): 1); ++ } ++ } ++ QSortFilterProxyModel::sort(m_sortType, m_sortOrder); ++} ++ ++ + QStringList FileItemProxyFilterSortModel::getAllFileUris() + { + QStringList l; +diff --git a/libpeony-qt/model/file-item-proxy-filter-sort-model.h b/libpeony-qt/model/file-item-proxy-filter-sort-model.h +index 93ddb0c..94b3655 100644 +--- a/libpeony-qt/model/file-item-proxy-filter-sort-model.h ++++ b/libpeony-qt/model/file-item-proxy-filter-sort-model.h +@@ -105,6 +105,8 @@ public: + void addFilterCondition(int option, int classify, bool updateNow = false); + void removeFilterCondition(int option, int classify, bool updateNow = false); + void clearConditions(); ++ void addFileContentFilter(QString key, bool updateNow = false); ++ void clearFileContentConditions(); + + //set file label filter conditions, default value mean all files are accepted + //use it without any paras can clear the filter conditions +@@ -129,6 +131,12 @@ public: + Qt::SortOrder expectedSortOrder(); + + void manualUpdateExpectedSortInfo(int sortType, Qt::SortOrder order); ++ void clearAllMapsCount(); ++ QMap<int, int> getFileTypeCount(); ++ QMap<int, int> getFileModifyTimeCount(); ++ QMap<int, int> getFileSizeCount(); ++ QMap<int, int> getFileLabelCount(); ++ void checkSettingsAndSort(); + + public Q_SLOTS: + void update(); +@@ -141,6 +149,7 @@ public Q_SLOTS: + + Q_SIGNALS: + void setSelectionModeChanged(); ++ void sortFinished(); + + protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; +@@ -153,9 +162,15 @@ private: + bool checkFileSizeFilter(quint64 size) const; + bool checkFileSizeOrTypeFilter(quint64 sizem, bool isDir) const; + bool checkFileNameFilter(const QString &displayName) const; ++ bool checkFileContentFilter(const QString &displayName) const; + + QVariant getDirectorySettings(const QString &key); + void setDirectorySettings(const QString &key, const QVariant &value); ++ void calculationFileTypeCount(QString type) const; ++ void calculationFileModifyTimeCount(quint64 modifiedTime) const; ++ void calculationFileSizeCount(quint64 size) const; ++ void calculationFileLabelCount(QStringList names, QList<QColor> colors) const; ++ void calculationAll(FileItem *item) const; + + private: + GlobalSettings *m_settings = nullptr; +@@ -172,6 +187,11 @@ private: + QColor m_label_color = Qt::transparent; + const int ALL_FILE = 0; + const quint64 K_BASE = 1024; ++ const quint64 TINY_BASE = 16 * K_BASE; ++ const quint64 SMALL_BASE = K_BASE * K_BASE; ++ const quint64 MEDIUM_BASE = 128 * K_BASE * K_BASE; ++ const quint64 BIG_BASE = K_BASE * K_BASE * K_BASE; ++ const quint64 LARGE_BASE = 4 * K_BASE * K_BASE * K_BASE; + int m_show_file_type=ALL_FILE, m_show_modify_time=ALL_FILE, m_show_file_size=ALL_FILE; + QList<int> m_file_type_list, m_modify_time_list, m_file_size_list; + QStringList m_file_name_list; +@@ -188,6 +208,12 @@ private: + Qt::SortOrder m_sortOrder = Qt::AscendingOrder; + + QDBusInterface *mDbusPeonyServer = nullptr; ++ QString m_fileContent; ++ ++ mutable QMap<int, int> m_file_type_map; ++ mutable QMap<int, int> m_file_modify_time_map; ++ mutable QMap<int, int> m_file_size_map; ++ mutable QMap<int, int> m_file_label_map; + }; + + } +diff --git a/libpeony-qt/model/file-item.cpp b/libpeony-qt/model/file-item.cpp +index 6639460..e50d83a 100644 +--- a/libpeony-qt/model/file-item.cpp ++++ b/libpeony-qt/model/file-item.cpp +@@ -102,7 +102,7 @@ FileItem::FileItem(std::shared_ptr<Peony::FileInfo> info, FileItem *parentItem, + QStringList favoriteUris; + if (m_uris_to_be_removed.count() < maxNumberOfDeletesByOne && !m_batchProcessThread->isRunning()) { + // do normal remove +- for (auto uri : m_uris_to_be_removed) { ++ for (auto& uri : m_uris_to_be_removed) { + for (int row = 0; row < m_children->count(); row++) { + auto child = m_children->at(row); + // 此处实际可靠性还有待验证 +@@ -119,6 +119,7 @@ FileItem::FileItem(std::shared_ptr<Peony::FileInfo> info, FileItem *parentItem, + m_model->endRemoveRows(); + FileLabelModel::getGlobalModel()->removeFileLabel(uri); + delete child; ++ child = nullptr; + break; + } + } +@@ -172,10 +173,12 @@ FileItem::~FileItem() + + for (auto child : *m_children) { + delete child; ++ child = nullptr; + } + m_children->clear(); + + delete m_children; ++ m_children = nullptr; + m_uri_item_hash.clear(); + + if (m_batchProcessThread->isRunning()) { +@@ -247,10 +250,15 @@ void FileItem::findChildrenAsync() + }); + enumerator->connect(enumerator, &FileEnumerator::prepared, this, [=](std::shared_ptr<GErrorWrapper> err, const QString &targetUri, bool critical) { + if (critical) { ++ QWidget *parent = nullptr; ++ if(this->parent() && this->parent()->parent() && this->parent()->parent()->property("KFileDialog").isValid()){ ++ QObject *obj = this->parent()->parent()->property("KFileDialog").value<QObject *>(); ++ parent = qobject_cast<QWidget *>(obj); ++ } + if (G_IO_ERROR_NOT_FOUND == err->code() || G_IO_ERROR_EXISTS == err->code()) { +- QMessageBox::warning(nullptr, tr("Warning"), err->message()); ++ QMessageBox::warning(parent, tr("Warning"), err->message()); + } else { +- QMessageBox::critical(nullptr, tr("Error"), err->message()); ++ QMessageBox::critical(parent, tr("Error"), err->message()); + } + //QMessageBox::critical(nullptr, tr("Error"), err->message()); + //Peony::AudioPlayManager::getInstance()->playWarningAudio(); +@@ -290,6 +298,11 @@ void FileItem::findChildrenAsync() + qDebug()<<"file item error:" <<err->message()<<enumerator->getEnumerateUri(); + //Peony::AudioPlayManager::getInstance()->playWarningAudio(); + ++ QWidget *parent = nullptr; ++ if(this->parent() && this->parent()->parent() && this->parent()->parent()->property("KFileDialog").isValid()){ ++ QObject *obj = this->parent()->parent()->property("KFileDialog").value<QObject *>(); ++ parent = qobject_cast<QWidget *>(obj); ++ } + //fix bug#214724, 214924, access smb-root error issue + if ((err.get()->code() == G_IO_ERROR_NOT_FOUND || err.get()->code() == G_IO_ERROR_PERMISSION_DENIED) && + this->uri() != "smb:///" && this->uri() != "network:///smb-root") { +@@ -310,7 +323,8 @@ void FileItem::findChildrenAsync() + auto fileInfo = FileInfo::fromUri(this->uri()); + if (err.get()->code() == G_IO_ERROR_NOT_FOUND && fileInfo->isSymbolLink()) + { +- auto result = QMessageBox::question(nullptr, tr("Open Link failed"), ++ ++ auto result = QMessageBox::question(parent, tr("Open Link failed"), + tr("File not exist, do you want to delete the link file?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + if (result == QMessageBox::Yes) { +@@ -322,7 +336,7 @@ void FileItem::findChildrenAsync() + } + else if (err.get()->code() == G_IO_ERROR_PERMISSION_DENIED) + { +- QMessageBox *msgBox = new QMessageBox(); ++ QMessageBox *msgBox = new QMessageBox(parent); + msgBox->setWindowTitle(tr("Error")); + QString errorInfo = tr("Can not open path \"%1\",permission denied.").arg(this->uri().unicode()); + msgBox->setText(errorInfo); +@@ -335,7 +349,7 @@ void FileItem::findChildrenAsync() + else if(err.get()->code() == G_IO_ERROR_NOT_FOUND) + { + QString errorInfo = tr("Can not find path \"%1\",are you moved or renamed it?").arg(fileInfo->uri().unicode()); +- QMessageBox::critical(nullptr, tr("Error"), errorInfo); ++ QMessageBox::critical(parent, tr("Error"), errorInfo); + } + enumerator->deleteLater(); + return; +@@ -343,7 +357,7 @@ void FileItem::findChildrenAsync() + else { + enumerator->cancel(); + enumerator->deleteLater(); +- QMessageBox::critical(nullptr, tr("Error"), err->message()); ++ QMessageBox::critical(parent, tr("Error"), err->message()); + return; + } + } +@@ -436,7 +450,9 @@ void FileItem::findChildrenAsync() + Q_EMIT this->deleted(uri); + this->onDeleted(uri); + }); +- ++ connect(m_watcher.get(), &FileWatcher::directoryAttrChanged, this, [=](QString uri) { ++ this->slot_directoryAttrChanged(uri); ++ }); + connect(m_watcher.get(), &FileWatcher::locationChanged, this, [=](QString oldUri, QString newUri) { + //this might use FileItemModel::setRootItem() + Q_EMIT this->renamed(oldUri, newUri); +@@ -520,6 +536,9 @@ void FileItem::findChildrenAsync() + Q_EMIT this->deleted(uri); + this->onDeleted(uri); + }); ++ connect(m_watcher.get(), &FileWatcher::directoryAttrChanged, this, [=](QString uri) { ++ this->slot_directoryAttrChanged(uri); ++ }); + connect(m_watcher.get(), &FileWatcher::locationChanged, this, [=](QString oldUri, QString newUri) { + //this might use FileItemModel::setRootItem() + Q_EMIT this->renamed(oldUri, newUri); +@@ -640,6 +659,7 @@ void FileItem::onDeleted(const QString &thisUri) + m_parent->onChildAdded(m_info->uri()); + } + this->deleteLater(); ++ m_model->updated(); + } else { + //cd up. + auto tmpItem = this; +@@ -662,7 +682,6 @@ void FileItem::onDeleted(const QString &thisUri) + m_model->sendPathChangeRequest("file:///", tmpItem->uri()); + } + } +- m_model->updated(); + } + + void FileItem::onRenamed(const QString &oldUri, const QString &newUri) +@@ -714,6 +733,23 @@ void FileItem::onChanged(const QString &uri) + } + } + ++void FileItem::slot_directoryAttrChanged(const QString &uri) ++{ ++ auto fileInfo = FileInfo::fromUri(uri); ++ auto infoJob = new FileInfoJob(fileInfo); ++ infoJob->setAutoDelete(); ++ connect(infoJob, &Peony::FileInfoJob::queryAsyncFinished, this, [=](bool successed){ ++ if(!successed) ++ return; ++ qDebug()<<"__func__<<__LINE__"<<fileInfo->uri()<<fileInfo->displayName(); ++ m_model->signal_updateTabPageTitle(uri); ++ m_model->signal_updateLocationBar(uri); ++ ++ }); ++ infoJob->connect(this, &FileItem::cancelFindChildren, infoJob, &FileInfoJob::cancel); ++ infoJob->queryAsync(); ++} ++ + void FileItem::onUpdateDirectoryRequest() + { + auto enumerator = new FileEnumerator(this); +@@ -780,6 +816,7 @@ void FileItem::updateInfoAsync() + //m_model->dataChanged(this->firstColumnIndex(), this->lastColumnIndex()); + m_model->updated(); + ThumbnailManager::getInstance()->createThumbnail(this->uri(), m_thumbnail_watcher, true); ++ Q_EMIT m_model->updateFilter(); + }); + + job->connect(this, &FileItem::cancelFindChildren, job, &FileInfoJob::cancel); +@@ -816,6 +853,7 @@ void FileItem::batchRemoveItems() + auto old = m_children; + m_children = children; + delete old; ++ old = nullptr; + m_uri_item_hash = uri_item_hash; + m_model->endResetModel(); + m_model->updated();/* 更新状态栏 */ +@@ -1000,6 +1038,12 @@ void FileItem::connectFunc() + item->setProperty("isFileForBurning", true); + } + #endif ++ ++ //Fix bug#232425 Connecting to shared folders with confusing file sorting ++ if (item->uri().startsWith("computer:///") && item->uri().endsWith(".mount")) { ++ return; ++ } ++ + m_model->beginInsertRows(QModelIndex(), m_children->count(), m_children->count()); + m_children->append(item); + m_uri_item_hash.insert(item->uri(), item); +@@ -1067,6 +1111,7 @@ void FileItem::clearChildren() + m_model->removeRows(0, m_model->rowCount(parent), parent); + for (auto child : *m_children) { + delete child; ++ child = nullptr; + } + m_children->clear(); + m_uri_item_hash.clear(); +@@ -1202,6 +1247,7 @@ void BatchProcessItems::slot_removeItems() + Q_EMIT removeItemsFinished(m_children, m_uri_item_hash, needHandleLabelUris); + for (auto child : itemsToBeDeleted) { + delete child; ++ child = nullptr; + } + int time1 = QTime::currentTime().msecsSinceStartOfDay(); + qDebug()<<"execute deletion finished, cost"<<time1 - time0; +diff --git a/libpeony-qt/model/file-item.h b/libpeony-qt/model/file-item.h +index cb66975..9634206 100644 +--- a/libpeony-qt/model/file-item.h ++++ b/libpeony-qt/model/file-item.h +@@ -110,6 +110,7 @@ public Q_SLOTS: + void onDeleted(const QString &thisUri); + void onRenamed(const QString &oldUri, const QString &newUri); + void onChanged(const QString &uri); ++ void slot_directoryAttrChanged(const QString &uri); + + void onUpdateDirectoryRequest(); + +diff --git a/libpeony-qt/model/file-label-model.cpp b/libpeony-qt/model/file-label-model.cpp +index 219d7a1..ec380b3 100644 +--- a/libpeony-qt/model/file-label-model.cpp ++++ b/libpeony-qt/model/file-label-model.cpp +@@ -199,9 +199,12 @@ void FileLabelModel::setLabelName(int id, const QString &name) + { + for (auto item : m_labels) { + if (item->id() == id) { ++ QString oldColorName = item->name(); + item->setName(name); + int row = m_labels.indexOf(item); + Q_EMIT dataChanged(index(row), index(row)); ++ QString labelUri = QString("label:///").append(QString::number(id)); ++ Q_EMIT labelColorNameChanged(labelUri, oldColorName, name); + break; + } + } +@@ -317,7 +320,7 @@ void FileLabelModel::addLabelToFile(const QString &uri, int labelId) + + /* 同步全局标记 */ + QUrl url(uri); +- QString labelUri = QString("label:///").append(getLabelNameFromLabelId(labelId)) + url.path() + "?schema=" + url.scheme(); ++ QString labelUri = QString("label:///").append(QString::number(labelId)) + url.path() + "?schema=" + url.scheme(); + Q_EMIT fileLabelAdded(labelUri, true);//end + + auto metaInfo = Peony::FileMetaInfo::fromUri(uri); +@@ -374,7 +377,7 @@ void FileLabelModel::removeFileLabel(const QString &uri, int labelId) + } + /* 同步全局标记 */ + QUrl url(uri); +- QString labelUri = QString("label:///").append(getLabelNameFromLabelId(id)) + url.path() + "?schema=" + url.scheme(); ++ QString labelUri = QString("label:///").append(QString::number(id)) + url.path() + "?schema=" + url.scheme(); + Q_EMIT fileLabelRemoved(labelUri, true); + } + m_label_settings->endGroup(); +@@ -482,6 +485,16 @@ int FileLabelModel::getLabelIdFromLabelName(const QString &colorName) + return 0; + } + ++QColor FileLabelModel::getLableColorFromLabelName(const QString &colorName) ++{ ++ for (auto item : m_labels) { ++ if (item->name() == colorName) { ++ return item->color(); ++ } ++ } ++ return QColor(Qt::transparent); ++} ++ + QString FileLabelModel::getLabelNameFromLabelId(int id) + { + for (auto item : m_labels) { +@@ -557,7 +570,7 @@ void FileLabelModel::updateLabesForAllFilesById(int id) + + for (auto uri : uriSet) { + QUrl url(uri); +- QString labelUri = QString("label:///").append(getLabelNameFromLabelId(id)) + url.path() + "?schema=" + url.scheme(); ++ QString labelUri = QString("label:///").append(QString::number(id)) + url.path() + "?schema=" + url.scheme(); + Q_EMIT fileLabelChanged(uri); + Q_EMIT fileLabelChanged(labelUri);/* 更新标识模式界面的该文件 */ + } +diff --git a/libpeony-qt/model/file-label-model.h b/libpeony-qt/model/file-label-model.h +index 4db36f0..d8f3178 100644 +--- a/libpeony-qt/model/file-label-model.h ++++ b/libpeony-qt/model/file-label-model.h +@@ -86,6 +86,7 @@ public: + QSet<QString> getFileUrisFromLabelId(int labelId); + QString getLabelNameFromLabelId(int id); + int getLabelIdFromLabelName(const QString &colorName); ++ QColor getLableColorFromLabelName(const QString &colorName); + + + Q_SIGNALS: +@@ -93,6 +94,7 @@ Q_SIGNALS: + void fileLabelAdded(const QString &uri, bool successed); + void fileLabelRemoved(const QString &uri, bool successed); + void fileLabelRenamed(const QString oldUri, const QString newUri); ++ void labelColorNameChanged(const QString uri, const QString& oldColorName, const QString& newColorName); + + public Q_SLOTS: + void setName(FileLabelItem *item, const QString &name); +diff --git a/libpeony-qt/model/side-bar-favorite-item.cpp b/libpeony-qt/model/side-bar-favorite-item.cpp +index 5e58867..057cc39 100644 +--- a/libpeony-qt/model/side-bar-favorite-item.cpp ++++ b/libpeony-qt/model/side-bar-favorite-item.cpp +@@ -92,7 +92,7 @@ SideBarFavoriteItem::SideBarFavoriteItem(QString uri,SideBarFavoriteItem *parent + void SideBarFavoriteItem::initChildren() + { + m_uri = "favorite:///"; +- m_displayName = tr("Quick access"); ++ m_displayName = tr("Quick Access"); + + QString desktopUri = localFileSystemPath + QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); + // QString videoUri = localFileSystemPath + QStandardPaths::writableLocation(QStandardPaths::MoviesLocation); +@@ -120,7 +120,8 @@ void SideBarFavoriteItem::initChildren() + //m_children->append(videoItem); + + +- if (FileUtils::isFileExsit("file:///data/usershare")) { ++ //story 28545, improve data block solution ++ if (FileUtils::isFileExsit("file:///data/usershare") && FileUtils::isDataBlockHasUserFile()) { + m_children->append(new SideBarFavoriteItem("favorite:///data/usershare?schema=file", this, m_model)); + } + +diff --git a/libpeony-qt/model/side-bar-file-system-item.cpp b/libpeony-qt/model/side-bar-file-system-item.cpp +index f89fc5f..e92c79f 100644 +--- a/libpeony-qt/model/side-bar-file-system-item.cpp ++++ b/libpeony-qt/model/side-bar-file-system-item.cpp +@@ -254,7 +254,7 @@ void SideBarFileSystemItem::initVolumeInfo(const Experimental_Peony::Volume &vol + if("file:///" == m_uri){ + m_unmountable = m_mountable = m_ejectable = m_stopable = false; + m_mounted = true; +- m_displayName = QObject::tr("File System"); ++ m_displayName = QObject::tr("System Disk"); + m_iconName = "drive-harddisk-system-symbolic"; + }else if("file:///data" == m_uri || isData){ + m_unmountable = m_mountable = m_ejectable = m_stopable = false; +@@ -289,6 +289,10 @@ void SideBarFileSystemItem::slot_volumeDeviceAdd(const Experimental_Peony::Volum + } + } + ++ if (FileUtils::isMountMatchFstab(addItem.getGVolume(), "/backup")) { ++ return; ++ } ++ + qDebug()<<__func__<<__LINE__<<addItem.device()<<addItem.getHidden(); + SideBarFileSystemItem *item = new SideBarFileSystemItem(nullptr, + addItem, +@@ -319,9 +323,10 @@ void SideBarFileSystemItem::slot_volumeDeviceRemove(const QString &removeDevice) + + void SideBarFileSystemItem::slot_volumeDeviceMount(const Experimental_Peony::Volume &volume) + { ++ qDebug()<<__func__<<__LINE__<<volume.device()<<volume.mountPoint()<<volume.getHidden()<<volume.icon(); + QString device = volume.device(); + QString mountPoint = volume.mountPoint(); +- if(mountPoint.isEmpty()) ++ if(device.isEmpty() || mountPoint.isEmpty()) + return; + + //过滤smb子项挂载后会更新computer:///ukui-data-volume,导致数据不准确 +@@ -341,6 +346,7 @@ void SideBarFileSystemItem::slot_volumeDeviceMount(const Experimental_Peony::Vol + item->m_unmountable = false; + } + item->m_iconName = volume.icon(); ++ + /* 更新uri,为了枚举操作 */ + if(device.startsWith("/dev/bus/usb"))/* 手机设备(mtp、gphoto2)的uri */ + item->m_uri = "computer:///" + volume.name() + ".volume"; +@@ -396,12 +402,11 @@ void SideBarFileSystemItem::slot_volumeDeviceUnmount(const QString &unmountDevic + void SideBarFileSystemItem::slot_volumeDeviceUpdate(const Experimental_Peony::Volume &updateDevice, QString property) + { + qDebug()<<__func__<<__LINE__<<updateDevice.device(); +- QString device; + if(property != "name") + return; + + auto gvolume = updateDevice.getGVolume(); +- device = updateDevice.device(); ++ QString device = updateDevice.device(); + for(auto& item:*m_children){ + if("file:///" == item->uri() || "computer:///ukui-data-volume" == item->uri())/* hotfix bug#125095 打开文件管理器后,插入U盘,侧边栏中文件系统消失 */ + continue; +@@ -415,7 +420,8 @@ void SideBarFileSystemItem::slot_volumeDeviceUpdate(const Experimental_Peony::Vo + item->m_displayName = updateDevice.name() + "(" + device + ")"; + item->m_hidden = updateDevice.getHidden(); + item->m_iconName = updateDevice.icon(); +- qDebug()<<__func__<<__LINE__<<item->m_device<<item->m_displayName<<item->m_hidden; ++ qDebug()<<__func__<<__LINE__<<item->m_device<<item->m_displayName<<item->m_hidden<<item->m_iconName; ++ + // 更新mount信息, 加密分区改变时需要 + g_autoptr (GMount) gmount = g_volume_get_mount(gvolume); + if (gmount) { +diff --git a/libpeony-qt/model/side-bar-net-work-item.cpp b/libpeony-qt/model/side-bar-net-work-item.cpp +index e428017..9772ce8 100644 +--- a/libpeony-qt/model/side-bar-net-work-item.cpp ++++ b/libpeony-qt/model/side-bar-net-work-item.cpp +@@ -220,6 +220,7 @@ void SideBarNetWorkItem::findChildren() + auto userShareManager = UserShareInfoManager::getInstance(); + connect(userShareManager, &UserShareInfoManager::signal_addSharedFolder, this, &SideBarNetWorkItem::slot_addSharedFolder); + connect(userShareManager, &UserShareInfoManager::signal_deleteSharedFolder, this, &SideBarNetWorkItem::slot_deleteSharedFolder); ++ connect(userShareManager, &UserShareInfoManager::signal_deleteUserShareList, this, &SideBarNetWorkItem::slot_deleteUserShareList); + connect(GlobalSettings::getInstance(), &GlobalSettings::signal_updateRemoteServer,this,&SideBarNetWorkItem::slot_updateRemoteServer, Qt::UniqueConnection); + + /* samba的子项 */ +@@ -421,6 +422,21 @@ void SideBarNetWorkItem::slot_serverMount(const Experimental_Peony::Volume &volu + } + } + ++void SideBarNetWorkItem::slot_deleteUserShareList(const QString &name) ++{ ++ for (auto item : *m_children){ ++ if(FileUtils::urlDecode(item->uri()).split("/").last() != FileUtils::urlDecode(name)) ++ continue; ++ int index = m_children->indexOf(item); ++ m_model->beginRemoveRows(firstColumnIndex(), index, index); ++ m_children->removeOne(item); ++ m_model->endRemoveRows(); ++ item->deleteLater(); ++ qDebug()<<"remove item for name:"<<FileUtils::urlDecode(name); ++ break; ++ } ++} ++ + void SideBarNetWorkItem::initWatcher() + { + /* 监听计算机视图的卸载信号 */ +diff --git a/libpeony-qt/model/side-bar-net-work-item.h b/libpeony-qt/model/side-bar-net-work-item.h +index 2822efe..3907da3 100644 +--- a/libpeony-qt/model/side-bar-net-work-item.h ++++ b/libpeony-qt/model/side-bar-net-work-item.h +@@ -124,6 +124,7 @@ private Q_SLOTS: + void slot_updateRemoteServer(const QString& server, bool add); + void slot_unmountedRemoteServerCallBack(const QString& server); + void slot_serverMount(const Experimental_Peony::Volume &volume); ++ void slot_deleteUserShareList(const QString& name); + + protected: + void initWatcher(); +diff --git a/libpeony-qt/mount-operation.cpp b/libpeony-qt/mount-operation.cpp +index 0041134..e9246da 100644 +--- a/libpeony-qt/mount-operation.cpp ++++ b/libpeony-qt/mount-operation.cpp +@@ -76,23 +76,32 @@ void MountOperation::start() + if (uri.scheme() != "mtp" && uri.toString() != "smb:///") { + ConnectServerLogin* dlg = new ConnectServerLogin(urit); + m_dlg = dlg; +- //block ui +- auto code = dlg->exec(); +- if (code == QDialog::Accepted) { ++ ++ if (!dlg->user().isEmpty() && !dlg->password().isEmpty() && !dlg->domain().isEmpty()) { + g_mount_operation_set_username(m_op, dlg->user().toUtf8().constData()); + g_mount_operation_set_password(m_op, dlg->password().toUtf8().constData()); + g_mount_operation_set_domain(m_op, dlg->domain().toUtf8().constData()); + g_mount_operation_set_anonymous(m_op, dlg->anonymous()); +- //TODO: when FileEnumerator::prepare(), trying mount volume without password dialog first. +- g_mount_operation_set_password_save(m_op, /*dlg->savePassword()? G_PASSWORD_SAVE_FOR_SESSION: */G_PASSWORD_SAVE_NEVER); +- } +- if (code == QDialog::Rejected) { +- cancel(); +- QMessageBox msg; +- msg.setIcon(QMessageBox::Critical); +- msg.setText(tr("Operation Cancelled")); +- msg.exec(); +- return; ++ g_mount_operation_set_password_save(m_op, G_PASSWORD_SAVE_NEVER); ++ dlg->setPassWordProperty(dlg->password()); ++ } else { ++ //block ui ++ auto code = dlg->exec(); ++ if (code == QDialog::Accepted) { ++ g_mount_operation_set_username(m_op, dlg->user().toUtf8().constData()); ++ g_mount_operation_set_password(m_op, dlg->password().toUtf8().constData()); ++ g_mount_operation_set_domain(m_op, dlg->domain().toUtf8().constData()); ++ g_mount_operation_set_anonymous(m_op, dlg->anonymous()); ++ //TODO: when FileEnumerator::prepare(), trying mount volume without password dialog first. ++ g_mount_operation_set_password_save(m_op, /*dlg->savePassword()? G_PASSWORD_SAVE_FOR_SESSION: */G_PASSWORD_SAVE_NEVER); ++ } ++ if (code == QDialog::Rejected) { ++ cancel(); ++ QMessageBox msg; ++ msg.setText(tr("Operation Cancelled")); ++ msg.exec(); ++ return; ++ } + } + } + +@@ -195,6 +204,26 @@ void MountOperation::ask_password_cb(GMountOperation *op, + auto code = p_this->m_dlg->exec(); + auto dlg = p_this->m_dlg; + if (code == QDialog::Accepted) { ++ /** ++ * @bug #214389: [File Manager] Login to remote share, only enter the user name to connect, the two prompts are inconsistent ++ * ++ * The value entered by the user is again checked again and if it is not legal, ++ * the ask_password_cb method is called recursively ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-11-21 ++ */ ++ // Check for anonymous login ++ if (dlg->anonymous()) { ++ g_mount_operation_set_anonymous(op, true); ++ g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED); ++ return; ++ } ++ // Validating user input for non-anonymous logins ++ if (dlg->user().isEmpty() || dlg->password().isEmpty()) { ++ ask_password_cb(op, message, default_user, default_domain, flags, p_this); ++ return; ++ } + g_mount_operation_set_username(op, dlg->user().toUtf8().constData()); + g_mount_operation_set_password(op, dlg->password().toUtf8().constData()); + g_mount_operation_set_domain(op, dlg->domain().toUtf8().constData()); +@@ -205,6 +234,10 @@ void MountOperation::ask_password_cb(GMountOperation *op, + } + if (code == QDialog::Rejected) { + g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED); ++ p_this->cancel(); ++ QMessageBox msg; ++ msg.setText(tr("Operation Cancelled")); ++ msg.exec(); + return; + } + } +diff --git a/libpeony-qt/org.ukui.peony.settings.gschema.xml b/libpeony-qt/org.ukui.peony.settings.gschema.xml +index 230c471..4915dcf 100644 +--- a/libpeony-qt/org.ukui.peony.settings.gschema.xml ++++ b/libpeony-qt/org.ukui.peony.settings.gschema.xml +@@ -2,6 +2,12 @@ + <schemalist> + <schema id="org.ukui.peony.settings" path="/org/ukui/peony/settings/"> + <!--General--> ++ <key type="b" name="desktop-use-auto-layout"> ++ <default>false</default> ++ <summary>setting layout mode for desktop icons</summary> ++ <description>Control the display of desktop icons, true for layout without space</description> ++ </key> ++ + <key type="b" name="trash-mobile-files"> + <default>false</default> + <summary>trash mobile files</summary> +@@ -164,6 +170,12 @@ + <description>This list stores disabled extensions</description> + </key> + ++ <key type="b" name="show-share-properties"> ++ <default>false</default> ++ <summary>show share properties</summary> ++ <description>Jump to Share Properties page</description> ++ </key> ++ + <!--directory-view--> + <key type="b" name="multi-select-checkbox-enabled"> + <default>true</default> +@@ -215,5 +227,29 @@ + <description>4 is ukui4.0 version, 3 is ukui3.0 version, other is ukui4.0</description> + </key> + ++ <key type="b" name="show-open-terminal"> ++ <default>true</default> ++ <summary>show open terminal</summary> ++ <description>Control show right menu open terminal option</description> ++ </key> ++ ++ <key type="b" name="enable-start-peony"> ++ <default>true</default> ++ <summary>enable start peony</summary> ++ <description>Control start peony or show peony UI</description> ++ </key> ++ ++ <key type="b" name="enable-double-click-desktop"> ++ <default>true</default> ++ <summary>enable double click desktop</summary> ++ <description>Control double click desktop files in desktop</description> ++ </key> ++ ++ <key type="b" name="enable-shortcut-keys"> ++ <default>true</default> ++ <summary>enable shortcut keys</summary> ++ <description>Control file operation of shortcut keys </description> ++ </key> ++ + </schema> + </schemalist> +diff --git a/libpeony-qt/peony-core.pri b/libpeony-qt/peony-core.pri +index 1c5ca01..cd52ade 100644 +--- a/libpeony-qt/peony-core.pri ++++ b/libpeony-qt/peony-core.pri +@@ -15,6 +15,7 @@ HEADERS += \ + $$PWD/connect-server-dialog.h \ + $$PWD/connect-to-server-dialog.h \ + $$PWD/private/file-vfs-info-private.h \ ++ $$PWD/tooltips-manager.h \ + $$PWD/volume-manager.h \ + $$PWD/gerror-wrapper.h \ + $$PWD/gobject-template.h \ +@@ -43,6 +44,7 @@ SOURCES += \ + $$PWD/connect-server-dialog.cpp \ + $$PWD/connect-to-server-dialog.cpp \ + $$PWD/private/file-vfs-info-private.cpp \ ++ $$PWD/tooltips-manager.cpp \ + $$PWD/volume-manager.cpp \ + $$PWD/gerror-wrapper.cpp \ + $$PWD/gobject-template.cpp \ +diff --git a/libpeony-qt/plugin-manager.cpp b/libpeony-qt/plugin-manager.cpp +index 0e027ae..7c9cb78 100644 +--- a/libpeony-qt/plugin-manager.cpp ++++ b/libpeony-qt/plugin-manager.cpp +@@ -73,11 +73,15 @@ PluginManager::PluginManager(QObject *parent) : QObject(parent) + // pluginsDir = QDir("/usr/lib/peony-qt-extensions"); + pluginsDir.setFilter(QDir::Files); + QStringList disabledExtensions = GlobalSettings::getInstance()->getValue(DISABLED_EXTENSIONS).toStringList(); ++ bool isCloudPlat = isCloudPlatform(); + + qDebug()<<pluginsDir.entryList().count(); + Q_FOREACH(QString fileName, pluginsDir.entryList(QDir::Files)) { + qDebug()<<fileName; + QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName)); ++ if (fileName == "libsafe-context-menu.so") { ++ pluginLoader.setLoadHints(pluginLoader.loadHints() | QLibrary::DeepBindHint); ++ } + qDebug()<<pluginLoader.fileName(); + qDebug()<<pluginLoader.metaData(); + qDebug()<<pluginLoader.load(); +@@ -94,6 +98,7 @@ PluginManager::PluginManager(QObject *parent) : QObject(parent) + if (!piface) + continue; + ++ bool isFileSafe = isFileSafePlugin(pluginLoader.metaData()); + QFileInfo fileInfo(pluginLoader.fileName()); + if (fileInfo.exists()) { + if (disabledExtensions.contains(fileInfo.fileName()) +@@ -116,6 +121,9 @@ PluginManager::PluginManager(QObject *parent) : QObject(parent) + if ("libpeony-drive-rename.so" == fileInfo.fileName()) { + qApp->setProperty("deviceRenamePluginLoaded", true); + } ++ if (pluginLoader.metaData().value("MetaData").toObject().value("pluginName").toString() == "PeonyFileSafePlugin") { ++ MenuPluginManager::getInstance()->insertFileSafePlugin(menuPlugin); ++ } + break; + } + case PluginInterface::PreviewPagePlugin: { +@@ -149,22 +157,9 @@ PluginManager::PluginManager(QObject *parent) : QObject(parent) + } + case PluginInterface::VFSPlugin: { + auto p = dynamic_cast<VFSPluginIface *>(plugin); +-#ifdef KY_SDK_SYSINFO +- if (p->name() == "file-safe vfs") { +- g_autofree char *isCloudPlat = kdk_system_get_hostVirtType(); +- if (isCloudPlat != nullptr) { +- qDebug() << "isCloudPlat is " << isCloudPlat; +- if (strcmp(isCloudPlat, "none") == 0) { +- VFSPluginManager::getInstance()->registerPlugin(p); +- } +- //delete isCloudPlat; +- } +- } else { ++ if (!isFileSafe || !isCloudPlat) { + VFSPluginManager::getInstance()->registerPlugin(p); + } +-#else +- VFSPluginManager::getInstance()->registerPlugin(p); +-#endif + break; + } + case PluginInterface::EmblemPlugin: { +@@ -180,7 +175,10 @@ PluginManager::PluginManager(QObject *parent) : QObject(parent) + default: + break; + } +- registerPlugin(piface, plugin); ++ ++ if (!isFileSafe || !isCloudPlat) { ++ registerPlugin(piface, plugin); ++ } + } + + connect(GlobalSettings::getInstance(), &GlobalSettings::valueChanged, this, [=](const QString &key){ +@@ -211,6 +209,7 @@ PluginManager::PluginManager(QObject *parent) : QObject(parent) + if (!piface) + continue; + ++ bool isFileSafe = isFileSafePlugin(pluginLoader.metaData()); + QFileInfo fileInfo(pluginLoader.fileName()); + if (fileInfo.exists()) { + if (disExtensions.contains(fileInfo.fileName()) && m_hash.keys().contains(piface->name()) +@@ -275,6 +274,9 @@ PluginManager::PluginManager(QObject *parent) : QObject(parent) + if ("libpeony-drive-rename.so" == fileInfo.fileName()) { + qApp->setProperty("deviceRenamePluginLoaded", true); + } ++ if (isFileSafe) { ++ MenuPluginManager::getInstance()->insertFileSafePlugin(menuPlugin); ++ } + break; + } + case PluginInterface::PreviewPagePlugin: { +@@ -450,6 +452,34 @@ void PluginManager::registerPlugin(PluginInterface *piface, QObject *plugin) + } + } + ++bool PluginManager::isFileSafePlugin(const QJsonObject &metaData) ++{ ++ return metaData.value("MetaData").toObject().value("pluginName").toString() == "PeonyFileSafePlugin"; ++} ++ ++bool PluginManager::isCloudPlatform() ++{ ++ bool isCloudPlat = false; ++#ifdef KY_SDK_SYSINFO ++ g_autofree char *cloudPlat = kdk_system_get_hostCloudPlatform(); ++ if (cloudPlat != nullptr) { ++ qDebug() << "cloudPlat is " << cloudPlat; ++ if (strcmp(cloudPlat, "none") != 0) { ++ isCloudPlat = true; ++ } ++ } ++ return isCloudPlat; ++#else ++ return isCloudPlat; ++#endif ++} ++ ++QList<MenuPluginInterface *> PluginManager::getComputerViewMenuPlugins() ++{ ++ auto plugins = MenuPluginManager::getInstance()->getComputerViewPlugins(); ++ return plugins.values(); ++} ++ + void PluginManager::init() + { + PluginManager::getInstance(); +diff --git a/libpeony-qt/plugin-manager.h b/libpeony-qt/plugin-manager.h +index f7ccf29..8fa5263 100644 +--- a/libpeony-qt/plugin-manager.h ++++ b/libpeony-qt/plugin-manager.h +@@ -30,6 +30,8 @@ + + namespace Peony { + ++class MenuPluginInterface; ++ + /*! + * \brief The PluginManager class + * \details +@@ -47,6 +49,9 @@ public: + void close(); + PluginInterface* getPluginByFileName(QString &fileName); + void registerPlugin(PluginInterface* piface, QObject* plugin); ++ bool isFileSafePlugin(const QJsonObject &metaData); ++ bool isCloudPlatform(); ++ QList<MenuPluginInterface *> getComputerViewMenuPlugins(); + + Q_SIGNALS: + void pluginStateChanged(const QString &pluginName, bool enable); +diff --git a/libpeony-qt/properties-window-factory-plugin-manager.cpp b/libpeony-qt/properties-window-factory-plugin-manager.cpp +index 481030f..95fc113 100644 +--- a/libpeony-qt/properties-window-factory-plugin-manager.cpp ++++ b/libpeony-qt/properties-window-factory-plugin-manager.cpp +@@ -136,14 +136,14 @@ bool PropertiesWindowFactoryPluginManager::unregisterFactory(PropertiesWindowTab + return false; + } + +-QMainWindow *PropertiesWindowFactoryPluginManager::create(const QStringList &uris) ++QMainWindow *PropertiesWindowFactoryPluginManager::create(const QStringList &uris, QWidget *parentWidget) + { + QString version = qApp->property("version").toString(); + if (version == "") { + version = "ukui4.0"; + } + PropertiesWindowFactoryPluginIface *iface = getFactory(version); +- return iface->create(uris); ++ return iface->create(uris, parentWidget); + } + + PropertiesWindowFactoryPluginIface *PropertiesWindowFactoryPluginManager::getFactory(const QString &id) +@@ -154,3 +154,13 @@ PropertiesWindowFactoryPluginIface *PropertiesWindowFactoryPluginManager::getFac + } + return factory; + } ++ ++void PropertiesWindowFactoryPluginManager::show() ++{ ++ QString version = qApp->property("version").toString(); ++ if (version == "") { ++ version = "ukui4.0"; ++ } ++ PropertiesWindowFactoryPluginIface *iface = getFactory(version); ++ return iface->show(); ++} +diff --git a/libpeony-qt/properties-window-factory-plugin-manager.h b/libpeony-qt/properties-window-factory-plugin-manager.h +index 2ab05d3..aac1435 100644 +--- a/libpeony-qt/properties-window-factory-plugin-manager.h ++++ b/libpeony-qt/properties-window-factory-plugin-manager.h +@@ -48,10 +48,12 @@ public: + bool registerFactory(PropertiesWindowTabPagePluginIface *factory); + bool unregisterFactory(PropertiesWindowTabPagePluginIface *factory); + +- QMainWindow *create(const QStringList &uris); ++ QMainWindow *create(const QStringList &uris, QWidget *parentWidget = nullptr); + + PropertiesWindowFactoryPluginIface *getFactory(const QString &id); + ++ void show(); ++ + explicit PropertiesWindowFactoryPluginManager(QObject *parent = nullptr); + + ~PropertiesWindowFactoryPluginManager() override; +diff --git a/libpeony-qt/sound-effect.cpp b/libpeony-qt/sound-effect.cpp +index 49d6b67..9e45af9 100644 +--- a/libpeony-qt/sound-effect.cpp ++++ b/libpeony-qt/sound-effect.cpp +@@ -81,11 +81,12 @@ void SoundEffect::playAlertSound(QString gsettingStr) + const gchar *desc = "Alert Sound"; + QString filenameStr; + QList<char *> existsPath = this->listExistsPath(); +- for (char * path : existsPath) { +- char * prepath = QString(KEYBINDINGS_CUSTOM_DIR).toLatin1().data(); +- char * allpath = strcat(prepath, path); ++ for (const char* path : existsPath) { ++ QString prepath = QString(KEYBINDINGS_CUSTOM_DIR); ++ QString allpath = prepath + QString::fromUtf8(path); ++ + const QByteArray ba(KEYBINDINGS_CUSTOM_SCHEMA); +- const QByteArray bba(allpath); ++ const QByteArray bba(allpath.toUtf8()); + if(QGSettings::isSchemaInstalled(ba)){ + QGSettings * settings = new QGSettings(ba, bba); + filenameStr = settings->get(FILENAME_KEY).toString(); +diff --git a/libpeony-qt/sync-thread.cpp b/libpeony-qt/sync-thread.cpp +index e212976..7a86e3a 100644 +--- a/libpeony-qt/sync-thread.cpp ++++ b/libpeony-qt/sync-thread.cpp +@@ -63,7 +63,7 @@ void SyncThread::notifyUser(QString notifyContent) + args << QObject::tr("File Manager").toUtf8().constData() + << ((unsigned int) 0) + << "system-file-manager" +- << tr("notify") ++ << tr("Notify") + << notifyContent + << actions + << hints +diff --git a/libpeony-qt/thumbnail-manager.cpp b/libpeony-qt/thumbnail-manager.cpp +index 00557d3..66562f7 100644 +--- a/libpeony-qt/thumbnail-manager.cpp ++++ b/libpeony-qt/thumbnail-manager.cpp +@@ -32,6 +32,7 @@ + #include "thumbnail/video-thumbnail.h" + #include "thumbnail/office-thumbnail.h" + #include "thumbnail/image-pdf-thumbnail.h" ++#include "thumbnail/text-plain-thumbnail.h" + #include "generic-thumbnailer.h" + #include "thumbnail-job.h" + +@@ -186,8 +187,8 @@ void ThumbnailManager::createPdfFileThumbnail(const QString &uri, std::shared_pt + QUrl url = uri; + + if (!uri.startsWith("file:///")) { +- url = FileUtils::getTargetUri(uri); +- //qDebug()<<url; ++ auto fileInfo = FileInfo::fromUri(uri); ++ url = fileInfo.get()->filePath(); + } + + PdfThumbnail pdfThumbnail(url.path()); +@@ -218,7 +219,7 @@ void ThumbnailManager::createImageFileThumbnail(const QString &uri, std::shared_ + if (watcher) { + watcher->fileChanged(uri); + } +- } else if (uri.startsWith("gphoto2://") || uri.startsWith("mtp://")) { ++ } else if (uri.startsWith("gphoto2://") || uri.startsWith("mtp://") || uri.startsWith("smb://")) { + //手机传输和图片传输需要重定向path后获取对应缩略图 + auto fileInfo = FileInfo::fromUri(uri); + QIcon thumbnail = GenericThumbnailer::generateThumbnail(fileInfo.get()->filePath(), true); +@@ -250,21 +251,46 @@ void ThumbnailManager::createOfficeFileThumbnail(const QString &uri, std::shared + return; + } + ++void ThumbnailManager::createTextFileThumbnail(const QString &uri, std::shared_ptr<FileWatcher> watcher) ++{ ++ QIcon thumbnail; ++ ++ textPlainThumbnail textThumbnail(uri); ++ thumbnail = textThumbnail.generateThumbnail();; ++ if (!thumbnail.isNull()) { ++ insertOrUpdateThumbnail(uri, thumbnail); ++ if (watcher) { ++ watcher->fileChanged(uri); ++ } ++ } ++ ++ return; ++} ++ + void ThumbnailManager::createDesktopFileThumbnail(const QString &uri, std::shared_ptr<FileWatcher> watcher) + { + QIcon thumbnail; + QUrl url = uri; +- QString path = url.path(); + + if (!uri.startsWith("file:///")) { + g_autoptr (GFile) gfile = g_file_new_for_uri(uri.toUtf8().constData()); +- g_autoptr (GFileInfo) gfileinfo = g_file_query_info(gfile, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, G_FILE_QUERY_INFO_NONE, 0, 0); +- g_autofree gchar *target_uri = g_file_info_get_attribute_as_string(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); +- if (target_uri) { +- url = QString(target_uri); ++ //g_autoptr (GFileInfo) gfileinfo = g_file_query_info(gfile, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, G_FILE_QUERY_INFO_NONE, 0, 0); ++ //g_autofree gchar *target_uri = g_file_info_get_attribute_as_string(gfileinfo, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); ++ g_autofree gchar *filePath = g_file_get_path(gfile); ++ if (filePath) { ++ url = QString(filePath); + } + } + ++ /** ++ * @bug #260905: [File Manager] The icon of the Youhong Reader shortcut deleted to the Recycle Bin is displayed abnormally ++ * ++ * If the uri doesn't start with “file:///”, get the path correctly after re-fetching the url ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-09-10 ++ */ ++ QString path = url.path(); + QString string; + g_autoptr (GDesktopAppInfo) desktop_app_info = g_desktop_app_info_new_from_filename(path.toUtf8().constData()); + if (desktop_app_info) { +@@ -393,6 +419,63 @@ void ThumbnailManager::findAtril() + void ThumbnailManager::createThumbnailInternal(const QString &uri, std::shared_ptr<FileWatcher> watcher, bool force) + { + // deprecated ++ auto settings = GlobalSettings::getInstance(); ++ if (settings->isExist(FORBID_THUMBNAIL_IN_VIEW)) { ++ bool do_not_thumbnail = settings->getValue(FORBID_THUMBNAIL_IN_VIEW).toBool(); ++ if (do_not_thumbnail && !force) { ++ qDebug()<<"setting is not thumbnail"; ++ return; ++ } ++ } ++ ++ //NOTE: we should do createThumbnail() after we have queried the file's info. ++ auto info = FileInfo::fromUri(uri); ++ //qDebug()<<"file uri:"<< uri << " mime type:" << info->mimeType(); ++ //qDebug()<<"file path:" << info->filePath(); ++ //qDebug()<<"file modify time:" << info->modifiedTime(); ++ ++ if (!info->mimeType().isEmpty()) { ++ if (!info->customIcon().isEmpty()) { ++ auto icon = GenericThumbnailer::generateThumbnail(info->customIcon()); ++ if (!icon.isNull()) { ++ insertOrUpdateThumbnail(uri, icon); ++ if (watcher) { ++ watcher->fileChanged(uri); ++ } ++ } ++ } ++ else if (info->isImagePdfFile()) ++ { ++ qDebug() <<"isImagePdfFile m_tril_exist:" <<m_tril_exist; ++ if (m_tril_exist) ++ { ++ createImagePdfFileThumbnail(uri, watcher); ++ } ++ } ++ else if (info->isImageFile()) { ++ createImageFileThumbnail(uri, watcher); ++ } ++ else if (info->mimeType().contains("pdf")) { ++ createPdfFileThumbnail(uri, watcher); ++ } ++ else if(info->isVideoFile()) { ++ createVideFileThumbnail(uri, watcher); ++ } ++ else if (info->isOfficeFile()) { ++ createOfficeFileThumbnail(uri, watcher); ++ } ++ else if (info->isTextFile()){ ++ //text file also use libreoffice create thumbnail,related to task#82064 ++ createTextFileThumbnail(uri, watcher); ++ } ++ else if (info->isDesktopFile()) { ++ createDesktopFileThumbnail(uri, watcher); ++ } ++ else { ++ //qDebug()<<"the file type: " << info->mimeType(); ++ //qDebug()<<"the mime type can not generate thumbnail."; ++ } ++ } + } + + void ThumbnailManager::createThumbnail(const QString &uri, std::shared_ptr<FileWatcher> watcher, bool force) +@@ -432,6 +515,9 @@ void ThumbnailManager::createThumbnail(const QString &uri, std::shared_ptr<FileW + else if (info->isOfficeFile()) { + needThumbnail = true; + } ++ else if (info->isTextFile()) { ++ needThumbnail = true; ++ } + else if (info->uri().endsWith(".desktop")) { + if (thumbnail.isNull()) + { +diff --git a/libpeony-qt/thumbnail-manager.h b/libpeony-qt/thumbnail-manager.h +index 46daa51..8091422 100644 +--- a/libpeony-qt/thumbnail-manager.h ++++ b/libpeony-qt/thumbnail-manager.h +@@ -45,6 +45,7 @@ class PEONYCORESHARED_EXPORT ThumbnailManager : public QObject + { + friend class UpdateThemedIconJob; + friend class ThumbnailJob; ++ friend class FileInfo; + Q_OBJECT + public: + static ThumbnailManager *getInstance(); +@@ -90,6 +91,7 @@ private: + void createImageFileThumbnail(const QString &uri, std::shared_ptr<FileWatcher> watcher); + void createOfficeFileThumbnail(const QString &uri, std::shared_ptr<FileWatcher> watcher); + void createDesktopFileThumbnail(const QString &uri, std::shared_ptr<FileWatcher> watcher); ++ void createTextFileThumbnail(const QString &uri, std::shared_ptr<FileWatcher> watcher); + + //djvu file process + void findAtril(); +diff --git a/libpeony-qt/thumbnail/generic-thumbnailer.cpp b/libpeony-qt/thumbnail/generic-thumbnailer.cpp +index 05e3733..bd1c8e1 100644 +--- a/libpeony-qt/thumbnail/generic-thumbnailer.cpp ++++ b/libpeony-qt/thumbnail/generic-thumbnailer.cpp +@@ -111,9 +111,19 @@ QIcon GenericThumbnailer::generateThumbnail(const QString &path, bool shadow, co + return icon; + + QSize targetSize = size.isValid()? size: QSize(128, 128); +- + QImage img = scaleImageWithAspectRatio(path, targetSize); + ++ //fix bug#268817, image thumbnail is null issue ++ if (img.isNull()) { ++ QFile file(path); ++ if (file.open(QIODevice::ReadOnly)){ ++ img.loadFromData(file.readAll()); ++ file.close(); ++ } ++ ++ img = img.scaled(targetSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); ++ } ++ + if (img.hasAlphaChannel()) { + //skip shadow + icon.addPixmap(QPixmap::fromImage(img)); +diff --git a/libpeony-qt/thumbnail/image-pdf-thumbnail.cpp b/libpeony-qt/thumbnail/image-pdf-thumbnail.cpp +index 16eaf2d..98ac88b 100644 +--- a/libpeony-qt/thumbnail/image-pdf-thumbnail.cpp ++++ b/libpeony-qt/thumbnail/image-pdf-thumbnail.cpp +@@ -35,7 +35,8 @@ + ImagePdfThumbnail::ImagePdfThumbnail(const QString &uri) + { + if (!uri.startsWith("file:///")) { +- m_url = FileUtils::getTargetUri(uri); ++ auto fileInfo = FileInfo::fromUri(uri); ++ m_url = fileInfo.get()->filePath(); + qDebug()<<"target uri:"<< m_url.path(); + } + else { +diff --git a/libpeony-qt/thumbnail/office-thumbnail.cpp b/libpeony-qt/thumbnail/office-thumbnail.cpp +index 5cecffa..953d414 100644 +--- a/libpeony-qt/thumbnail/office-thumbnail.cpp ++++ b/libpeony-qt/thumbnail/office-thumbnail.cpp +@@ -35,7 +35,8 @@ + OfficeThumbnail::OfficeThumbnail(const QString &uri) + { + if (!uri.startsWith("file:///")) { +- m_url = FileUtils::getTargetUri(uri); ++ auto fileInfo = FileInfo::fromUri(uri); ++ m_url = fileInfo.get()->filePath(); + qDebug()<<"target uri:"<< m_url.path(); + } + else { +@@ -124,7 +125,7 @@ QIcon OfficeThumbnail::generateThumbnail() + } + + QString err=p.readAllStandardError(); +- QString read=p.readAll(); ++ //QString read=p.readAll(); + if (!err.isEmpty()) { + qWarning()<<"office convert jpg error: " << err; + return thumbnailImage; +diff --git a/libpeony-qt/thumbnail/text-plain-thumbnail.cpp b/libpeony-qt/thumbnail/text-plain-thumbnail.cpp +new file mode 100644 +index 0000000..7267c7f +--- /dev/null ++++ b/libpeony-qt/thumbnail/text-plain-thumbnail.cpp +@@ -0,0 +1,255 @@ ++/* ++ * Peony-Qt's Library ++ * ++ * Copyright (C) 2022, KylinSoft Co., Ltd. ++ * ++ * 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 3 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 General Public License ++ * along with this library. If not, see <https://www.gnu.org/licenses/>. ++ * ++ * Authors: hemeihong <hemeihong@kylinos.cn> ++ * ++ */ ++ ++#include "text-plain-thumbnail.h" ++#include "generic-thumbnailer.h" ++#include "file-utils.h" ++#include <QFileInfo> ++#include <QDebug> ++#include <QtConcurrent> ++#include <QImage> ++#include <QMessageAuthenticationCode> ++#include <QPainter> ++#include <QImageReader> ++#include <QFile> ++#include <QFont> ++#include <QPen> ++#include <QPainter> ++#include <QTextOption> ++#include <qglobal.h> ++#include <QPainterPath> ++ ++textPlainThumbnail::textPlainThumbnail(const QString &uri) ++{ ++ if (!uri.startsWith("file:///")) { ++ auto fileInfo = FileInfo::fromUri(uri); ++ m_url = fileInfo.get()->filePath(); ++ qDebug()<<"target uri:"<< m_url.path(); ++ } ++ else { ++ m_url = uri; ++ } ++ ++ auto fileInfo = FileInfo::fromUri(uri); ++ m_modifyTime = fileInfo->modifiedTime(); ++} ++ ++textPlainThumbnail::~textPlainThumbnail() ++{ ++ ++} ++ ++/* ++*函数功能: ++现将文本文档转换为PDF文档,再转换为jpg文件,直接转换为图片会失败 ++*/ ++QIcon textPlainThumbnail::generateThumbnail() ++{ ++ QIcon thumbnailImage; ++ QString md5Name=GenericThumbnailer::codeMd5WithModifyTime(m_url.path(), m_modifyTime); ++ QString thumbnail_dir= GenericThumbnailer::thumbnaileCachDir() + "/" + md5Name; ++ QString fileName = m_url.fileName(); ++ qint16 idx = fileName.lastIndexOf("."); ++ QString fileThumbnail=thumbnail_dir + "/" + fileName.left(idx) + ".jpg"; ++ ++ QImage image= gernerateTextImage(); ++ if (! image.isNull()) { ++ QPixmap pixmap = QPixmap::fromImage(image); ++ //thumbnailImage.addPixmap(pixmap); ++ thumbnailImage = QIcon(pixmap); ++ return thumbnailImage; ++ } ++ ++ //优化无效流程,未安装libreoffice则直接返回 ++ if (! QFile::exists("/usr/bin/libreoffice")) ++ { ++ qDebug()<<"libreoffice not installed, textPlainThumbnail return"; ++ return thumbnailImage; ++ } ++ ++ qDebug()<<"file thumbnail:"<<fileThumbnail; ++ if (!QFile::exists(fileThumbnail)) { ++ //libreoffice --convert-to PDF --convert-to jpg:writer_jpg_Export test1.txt --outdir ./ ++ QStringList list; ++ //text file also use libreoffice create thumbnail,related to task#82064 ++ //注意txt文档需要先转换为pdf文档再转图片,直接转图片会生成缩略图失败 ++ list<<"--headless" /*headless和invisible的方式可以避免出现界面以及无用的log信息,速度更快*/ ++ <<"--invisible" ++ //新版本可以直接txt文档转图片 ++// <<"--convert-to" ++// <<"PDF" /*老版本libreoffice,txt文档先转换为PDF文档再转图片,直接转图片会失败*/ ++ <<"--convert-to" ++ <<"jpg:writer_jpg_Export" /*转换格式jpg*/ ++ <<m_url.path() /*要转换的文件*/ ++ <<"--outdir" /*转换完的jpg文件存在的路径*/ ++ <<thumbnail_dir; ++ ++ qDebug()<<"the libreoffice cmd: " << list; ++ ++ QProcess p; ++ p.start("/usr/bin/libreoffice",list); ++ ++ /* ++ * 等待30s超时,30s是默认时间,可以修改 ++ */ ++ if (!p.waitForStarted()) { ++ qWarning()<<"libreoffice start failed, or timeout"; ++ return thumbnailImage; ++ } ++ ++ /* ++ * 等待30s超时,30s是默认时间,可以修改 ++ */ ++ if (!p.waitForFinished()) { ++ qWarning()<<"libreoffice run failed, or timeout"; ++ return thumbnailImage; ++ } ++ ++ QString err=p.readAllStandardError(); ++ //QString read=p.readAll(); ++ if (!err.isEmpty()) { ++ qWarning()<<"office convert jpg error: " << err; ++ return thumbnailImage; ++ } ++ } ++ ++ /* ++ *不知道会不会出现无效的图片的情况,可能需要对这种情况做处理? ++ */ ++ thumbnailImage = GenericThumbnailer::generateThumbnail(fileThumbnail, true); ++ ++ return thumbnailImage; ++} ++ ++/* ++*函数功能: ++现将文本文档读取开头部分内容,绘制生成图片供缩略图使用 ++解决使用libreofice文本转图片看不清内容问题 ++同时可以不依赖libreofice库接口 ++*/ ++QImage textPlainThumbnail::gernerateTextImage() ++{ ++ QImage thumbnailImg; ++ const int imageSize = 128; ///< Final thumbnail size ++ const int leftMargin = 15; ///< Left margin of content area ++ const int rightMargin = 18; ///< Right margin of content area ++ const int topMargin = 1; ///< Top margin of content area ++ const int bottomMargin = 8; ///< Bottom margin of content area ++ const int cornerRadius = 12; ///< Corner radius for rounded rectangle ++ const int textPadding = 5; ///< Padding between text and border ++ const int maxReadSize = 2000; ///< Maximum bytes to read from file ++ ++ // Calculate content area dimensions ++ const int contentWidth = imageSize - leftMargin - rightMargin; ++ const int contentHeight = imageSize - topMargin - bottomMargin; ++ ++ // Open and validate file ++ QFile file(m_url.path()); ++ if (!file.exists() || !file.open(QIODevice::ReadOnly)) { ++ qWarning() << "thumbnail: can not open this file." << m_url.path(); ++ return thumbnailImg; ++ } ++ ++ // Check file size ++ QFileInfo fileInfo(file); ++ if (fileInfo.size() <= 0) { ++ qWarning() << "thumbnail: file is empty." << m_url.path(); ++ return thumbnailImg; ++ } ++ ++ // Read and decode file content with size limitation ++ QByteArray content = file.read(maxReadSize); ++ QString text; ++ QTextCodec *codec = QTextCodec::codecForName("UTF-8"); ++ if (!codec) { ++ codec = QTextCodec::codecForLocale(); ++ } ++ text = codec->toUnicode(content); ++ ++ // Validate text content ++ if (text.isEmpty()) { ++ qWarning() << "thumbnail: text content is empty." << m_url.path(); ++ return thumbnailImg; ++ } ++ ++ // Create final transparent image ++ thumbnailImg = QImage(imageSize, imageSize, QImage::Format_ARGB32_Premultiplied); ++ thumbnailImg.fill(Qt::transparent); ++ ++ // Create content area image ++ QImage contentImg(contentWidth, contentHeight, QImage::Format_ARGB32_Premultiplied); ++ contentImg.fill(Qt::transparent); ++ ++ // Setup content painter with high quality rendering hints ++ QPainter contentPainter(&contentImg); ++ contentPainter.setRenderHint(QPainter::Antialiasing, true); ++ contentPainter.setRenderHint(QPainter::TextAntialiasing, true); ++ contentPainter.setRenderHint(QPainter::SmoothPixmapTransform, true); ++ contentPainter.setRenderHint(QPainter::HighQualityAntialiasing, true); ++ ++ // Create and draw rounded rectangle path ++ QPainterPath path; ++ path.addRoundedRect(0.5, 0.5, contentWidth - 1, contentHeight - 1, ++ cornerRadius, cornerRadius); ++ ++ // Fill white background ++ contentPainter.fillPath(path, Qt::white); ++ ++ // Draw light border ++ QPen borderPen(QColor(0, 0, 0, 25)); ///< Semi-transparent black for border ++ borderPen.setWidth(1); ++ contentPainter.setPen(borderPen); ++ contentPainter.drawPath(path); ++ ++ // Set clip path to contain text within rounded corners ++ contentPainter.setClipPath(path); ++ ++ // Configure font settings ++ QFont font; ++ font.setPixelSize(13); ++ font.setHintingPreference(QFont::PreferFullHinting); ++ font.setStyleStrategy(QFont::PreferQuality); ++ contentPainter.setFont(font); ++ contentPainter.setPen(Qt::black); ++ ++ // Configure text layout options ++ QTextOption option; ++ option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); ++ option.setAlignment(Qt::AlignLeft | Qt::AlignTop); ++ ++ // Calculate text area with padding ++ QRect textRect = contentImg.rect().adjusted(textPadding, textPadding, ++ -textPadding, -textPadding); ++ ++ // Draw text content ++ contentPainter.drawText(textRect, text, option); ++ contentPainter.end(); ++ ++ // Draw content onto final image ++ QPainter finalPainter(&thumbnailImg); ++ finalPainter.setRenderHint(QPainter::Antialiasing, true); ++ finalPainter.drawImage(leftMargin, topMargin, contentImg); ++ finalPainter.end(); ++ ++ file.close(); ++ return thumbnailImg; ++} +diff --git a/libpeony-qt/thumbnail/text-plain-thumbnail.h b/libpeony-qt/thumbnail/text-plain-thumbnail.h +new file mode 100644 +index 0000000..ebd2e85 +--- /dev/null ++++ b/libpeony-qt/thumbnail/text-plain-thumbnail.h +@@ -0,0 +1,56 @@ ++/* ++ * Peony-Qt's Library ++ * ++ * Copyright (C) 2022, KylinSoft Co., Ltd. ++ * ++ * 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 3 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 General Public License ++ * along with this library. If not, see <https://www.gnu.org/licenses/>. ++ * ++ * Authors: hemeihong <hemeihong@kylinos.cn> ++ * ++ */ ++ ++#ifndef TEXTPLAINTHUMBNAIL_H ++#define TEXTPLAINTHUMBNAIL_H ++ ++#include "file-info.h" ++#include <QHash> ++#include <QIcon> ++#include <QMutex> ++#include <QUrl> ++ ++using namespace Peony; ++ ++class textPlainThumbnail ++{ ++public: ++ explicit textPlainThumbnail(const QString &uri); ++ ~textPlainThumbnail(); ++ QIcon generateThumbnail(); ++ QImage gernerateTextImage(); ++ ++private: ++ /* ++ * 提供文本文件首页转换为图片的存储路径 ++ */ ++ void thumbnaileCachDir(); ++ ++ QUrl m_url; ++ /* ++ * 获取文件的修改时间,如果被修改,将重新生成缩略图, ++ * 主要是为了处理修改文件首页的情况 ++ */ ++ quint64 m_modifyTime = 0; ++}; ++ ++#endif // TEXTPLAINTHUMBNAIL_H +diff --git a/libpeony-qt/thumbnail/thumbnail-job.cpp b/libpeony-qt/thumbnail/thumbnail-job.cpp +index 2086b69..368606d 100644 +--- a/libpeony-qt/thumbnail/thumbnail-job.cpp ++++ b/libpeony-qt/thumbnail/thumbnail-job.cpp +@@ -79,19 +79,6 @@ Peony::ThumbnailJob::~ThumbnailJob() + + void Peony::ThumbnailJob::run() + { +- /* 移动设备弹出时被ffmpeg占用时,强制弹出过程中防止该设备的文件仍继续使用ffmpeg,link to bug#117263 */ +- { +- auto mutex = Experimental_Peony::VolumeManager::getInstance()->getMutex(); +- QMutexLocker lk(mutex); +- auto occupiedVolume = Experimental_Peony::VolumeManager::getInstance()->getOccupiedVolume(); +- if(occupiedVolume){ +- QString volumeUri = Experimental_Peony::VolumeManager::getInstance()->getTargetUriFromUnixDevice(occupiedVolume->device()); +- qDebug()<<occupiedVolume->device()<<volumeUri<<m_uri; +- if(m_uri.startsWith(volumeUri)) +- return; +- } +- }//end +- + if (!parent()) + return; + +@@ -103,6 +90,19 @@ void Peony::ThumbnailJob::run() + return; + } + ++ /* 移动设备弹出时被ffmpeg占用时,强制弹出过程中防止该设备的文件仍继续使用ffmpeg,link to bug#117263 */ ++ if(!GlobalSettings::getInstance()->isDesktopStartUp()){ ++ auto mutex = Experimental_Peony::VolumeManager::getInstance()->getMutex(); ++ QMutexLocker lk(mutex); ++ auto occupiedVolume = Experimental_Peony::VolumeManager::getInstance()->getOccupiedVolume(); ++ if(occupiedVolume){ ++ QString occupiedVolumeUri = Experimental_Peony::VolumeManager::getInstance()->getOccupiedVolumeUri(); ++ qDebug()<<occupiedVolume->device()<<occupiedVolumeUri<<m_uri; ++ if(!occupiedVolumeUri.isEmpty() && m_uri.startsWith(occupiedVolumeUri)) ++ return; ++ } ++ }//end ++ + runCount++; + + qDebug()<<"job start, current end:"<<endCount<<"current start request:"<<runCount<<"uri:"<<m_uri; +@@ -131,12 +131,13 @@ void Peony::ThumbnailJob::run() + } else if (mimeType.contains("djvu")) { + setType(ImagePdf); + } else if (mimeType.startsWith("video") +- || mimeType.endsWith("vnd.trolltech.linguist") + || mimeType.endsWith("vnd.adobe.flash.movie") + || mimeType.endsWith("vnd.rn-realmedia") +- || mimeType.endsWith("vnd.ms-asf") +- || mimeType.endsWith("octet-stream")) { ++ || mimeType.endsWith("vnd.ms-asf")) { + setType(Video); ++ } else if (mimeType.contains("text/plain") && m_uri.endsWith(".txt")) { ++ //task#383521, support pure txt file preview ++ setType(TextPlain); + } else { + int idx = 0; + QString mtype = nullptr; +@@ -195,6 +196,11 @@ void Peony::ThumbnailJob::run() + } + break; + } ++ case TextPlain: { ++ //task#383521, support pure txt file preview ++ ThumbnailManager::getInstance()->createTextFileThumbnail(m_uri, strongPtr); ++ break; ++ } + default: { + break; + } +diff --git a/libpeony-qt/thumbnail/thumbnail-job.h b/libpeony-qt/thumbnail/thumbnail-job.h +index 50ec718..2237c04 100644 +--- a/libpeony-qt/thumbnail/thumbnail-job.h ++++ b/libpeony-qt/thumbnail/thumbnail-job.h +@@ -48,6 +48,7 @@ public: + Office = 6, + Desktop = 7, + CustomIcon = 8, ++ TextPlain = 9, + Other + }; + Q_ENUM (Type) +@@ -67,6 +68,7 @@ public Q_SLOTS: + private: + QString m_uri; + std::weak_ptr<FileWatcher> m_watcher; ++ + }; + + } +diff --git a/libpeony-qt/thumbnail/thumbnail.pri b/libpeony-qt/thumbnail/thumbnail.pri +index 8348a31..3690336 100644 +--- a/libpeony-qt/thumbnail/thumbnail.pri ++++ b/libpeony-qt/thumbnail/thumbnail.pri +@@ -3,6 +3,7 @@ INCLUDEPATH += $$PWD + HEADERS += $$PWD/pdf-thumbnail.h \ + $$PWD/generic-thumbnailer.h \ + $$PWD/image-pdf-thumbnail.h \ ++ $$PWD/text-plain-thumbnail.h \ + $$PWD/thumbnail-job.h \ + $$PWD/video-thumbnail.h \ + $$PWD/office-thumbnail.h +@@ -10,6 +11,7 @@ HEADERS += $$PWD/pdf-thumbnail.h \ + SOURCES += $$PWD/pdf-thumbnail.cpp \ + $$PWD/generic-thumbnailer.cpp \ + $$PWD/image-pdf-thumbnail.cpp \ ++ $$PWD/text-plain-thumbnail.cpp \ + $$PWD/thumbnail-job.cpp \ + $$PWD/video-thumbnail.cpp \ + $$PWD/office-thumbnail.cpp +diff --git a/libpeony-qt/thumbnail/video-thumbnail.cpp b/libpeony-qt/thumbnail/video-thumbnail.cpp +index cd90f34..93870b0 100644 +--- a/libpeony-qt/thumbnail/video-thumbnail.cpp ++++ b/libpeony-qt/thumbnail/video-thumbnail.cpp +@@ -35,7 +35,8 @@ + VideoThumbnail::VideoThumbnail(const QString &uri) + { + if (!uri.startsWith("file:///")) { +- m_url = FileUtils::getTargetUri(uri); ++ auto fileInfo = FileInfo::fromUri(uri); ++ m_url = fileInfo.get()->filePath(); + qDebug()<<"target uri:"<< m_url.path(); + } + else { +@@ -127,80 +128,56 @@ QMap<QString, QString> VideoThumbnail::videoInfo() + return map; + } + +-/* +-* 函数功能: +-* 通过ffmpeg从视频文件中提取出缩略图显示的图片,该图片存放到tmp目录下,不会主动删除, +-* tmp是内存文件系统,系统重启会自动清除图片 +-* +-* 性能测试: +-* 转化性能和文件大小以及视频文件格式有关。在V10上面测试ffmpeg不支持mpeg格式的视频文件 +-* 这个可能和解码器的配置有关,可以通过视频格式转换后,再提取图片,效率很低,暂时未实现。 +-* +-* 后续优化方式: +-* 通过调用ffpmeg的api实现视频格式转换和图片文件提取,需要验证性能的提升情况。 +-*/ + QIcon VideoThumbnail::generateThumbnail() + { + QIcon thumbnailImage; +- QString thumbnail= GenericThumbnailer::thumbnaileCachDir(); +- QString md5Name=GenericThumbnailer::codeMd5WithModifyTime(m_url.path(), m_modifyTime); +- QString fileThumbnail=thumbnail+"/"+md5Name; +- ++ static const QString thumbnailDir = GenericThumbnailer::thumbnaileCachDir(); ++ const QString md5Name = GenericThumbnailer::codeMd5WithModifyTime(m_url.path(), m_modifyTime); ++ const QString fileThumbnail = thumbnailDir + "/" + md5Name; ++ ++ /** ++ * @bug #192691: [M900] [Audio/Video] The probability of adding a video will be stuttered, ++ * and the added video will not be played until it has stuttered for more than 20s (Probability of recurrence: 2/10) ++ * ++ * Use the ffmpegthumbnailer command to get the thumbnail of a video file to improve the efficiency of getting it. ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-10-21 ++ */ ++ // Check if thumbnail already exists + if (!QFile::exists(fileThumbnail)) { +- static bool isWayland = qApp->property("isWayland").toBool(); +- QStringList list; +- if (isWayland) { +- QMap<QString, QString> map= videoInfo(); +- QString pos=map.value("Pos"); +- +- //ffmpeg -i ./kofar-bi-amirica.mp4 -y -ss 10.0 -vframes 1 -f image2 -s 128x128 thumbnail +- list<<"-hwaccel"<<"auto" /*try using hardware accel*/ +- <<"-i"<<m_url.path() /*Input File Name*/ +- <<"-y" /*Overwrite*/ +- <<"-ss"<<pos /* seeks in this position*/ +- <<"-vframes"<<"1" /* Num Frames */ +- <<"-f"<<"image2" /* file format. */ +- // <<"-s"<<"128x128" /*<<"-vf"<<scal*/ +- <<"-s"<<"640x640" /*<<"-vf"<<scal*/ +- <<fileThumbnail; /*output file Name */ +- qDebug()<<"the ffmpeg cmd: " << list; +- } else { +- //ffmpeg -i ./kofar-bi-amirica.mp4 -y -ss 10.0 -vframes 1 -f image2 -s 128x128 thumbnail +- list<<"-i"<<m_url.path() /*Input File Name*/ +- <<"-y" /*Overwrite*/ +- <<"-ss"<<"10.0" /* seeks in this position*/ +- <<"-vframes"<<"1" /* Num Frames */ +- <<"-f"<<"image2" /* file format. */ +- // <<"-s"<<"128x128" /*<<"-vf"<<scal*/ +- <<"-s"<<"640x640" /*<<"-vf"<<scal*/ +- <<fileThumbnail; /*output file Name */ +- qDebug()<<"the ffmpeg cmd: " << list; +- } +- +- QProcess p; +- p.start("/usr/bin/ffmpeg",list); +- +- if (!p.waitForStarted()) { +- qWarning()<<"start get video image failed."; +- return thumbnailImage; ++ QStringList arguments; ++ arguments << "-i" << m_url.path() // Input file ++ << "-o" << fileThumbnail // Output file ++ << "-s" << "640"; // Thumbnail size ++ // Uncomment the following lines to add more options ++ // << "-t" << "10%" // Seek to 10% of the video ++ // << "-q" << "8" // JPEG quality ++ // << "-f"; // Add film strip effect ++ ++ QProcess ffmpegProcess; ++ ffmpegProcess.start("ffmpegthumbnailer", arguments); ++ ++ // Wait for the process to start ++ if (!ffmpegProcess.waitForStarted()) { ++ qWarning() << "Failed to start ffmpegthumbnailer process."; + } + +- if (!p.waitForFinished(40000)) { +- qWarning()<<"wait video image too long time."; +- return thumbnailImage; ++ // Wait for the process to finish with a timeout ++ constexpr int timeout = 10000; // 10 seconds ++ if (!ffmpegProcess.waitForFinished(timeout)) { ++ qWarning() << "ffmpegthumbnailer process timed out."; ++ ffmpegProcess.kill(); + } + +- QString err=p.readAllStandardError(); +- QString read=p.readAll(); +- if (err.contains("not contain any stream")) { +- qWarning()<<"get video image failed."; +- return thumbnailImage; ++ // Check for any errors ++ const QString errorOutput = ffmpegProcess.readAllStandardError(); ++ if (!errorOutput.isEmpty()) { ++ qWarning() << "ffmpegthumbnailer process reported an error:" << errorOutput; + } + } + +- /* +- *不知道会不会出现无效的图片的情况,可能需要对这种情况做处理 +- */ ++ // Generate the thumbnail icon + thumbnailImage = GenericThumbnailer::generateThumbnail(fileThumbnail, true); + + return thumbnailImage; +diff --git a/libpeony-qt/thumbnail/video-thumbnail.h b/libpeony-qt/thumbnail/video-thumbnail.h +index 5479a45..95cc018 100644 +--- a/libpeony-qt/thumbnail/video-thumbnail.h ++++ b/libpeony-qt/thumbnail/video-thumbnail.h +@@ -35,6 +35,15 @@ class VideoThumbnail{ + public: + explicit VideoThumbnail(const QString &uri); + ~VideoThumbnail(); ++ /** ++ * @brief Generates a thumbnail for a video file. ++ * ++ * This function attempts to create a thumbnail for the video file specified ++ * by the class member m_url. It first checks if a cached thumbnail exists, ++ * and if not, uses ffmpegthumbnailer to generate one. ++ * ++ * @return QIcon The generated thumbnail as a QIcon. Returns an empty QIcon if generation fails. ++ */ + QIcon generateThumbnail(); + + private: +diff --git a/libpeony-qt/tooltips-manager.cpp b/libpeony-qt/tooltips-manager.cpp +new file mode 100644 +index 0000000..f6075cd +--- /dev/null ++++ b/libpeony-qt/tooltips-manager.cpp +@@ -0,0 +1,275 @@ ++/* ++ * Peony-Qt ++ * ++ * Copyright (C) 2024, Tianjin KYLIN Information Technology Co., Ltd. ++ * ++ * 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 3 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, see <https://www.gnu.org/licenses/>. ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-09-23 14:55 ++ */ ++ ++#include "tooltips-manager.h" ++#include "file-info.h" ++#include "file-utils.h" ++#include "global-settings.h" ++ ++#include <QUrl> ++#include <QSet> ++#include <QImageReader> ++#include <QVariant> ++#include <QFileInfo> ++#include <QProcess> ++#include <QStandardPaths> ++ ++namespace Peony { ++ ++// HTML templates for tooltips ++static const QString CONTENT_TEMPLATE_WITH_FULLNAME = R"( ++<html> ++ <head> ++ <style> ++ table { width: 200px; border-collapse: collapse; } ++ td { word-wrap: break-word; max-width: 0; } ++ </style> ++ </head> ++ <body> ++ <table> ++ <tr><td>%1</td></tr> ++ <tr><td>%2%3</td></tr> ++ <tr><td>%4%5</td></tr> ++ <tr><td>%6%7</td></tr> ++ </table> ++ </body> ++</html> ++)"; ++ ++static const QString CONTENT_TEMPLATE_WITHOUT_FULLNAME = R"( ++<html> ++ <head> ++ <style> ++ table { width: 200px; border-collapse: collapse; } ++ td { word-wrap: break-all; max-width: 0; } ++ </style> ++ </head> ++ <body> ++ <table> ++ <tr><td>%1%2</td></tr> ++ <tr><td>%3%4</td></tr> ++ <tr><td>%5%6</td></tr> ++ </table> ++ </body> ++</html> ++)"; ++ ++TooltipsManager& TooltipsManager::instance() ++{ ++ static TooltipsManager instance; ++ return instance; ++} ++ ++QString TooltipsManager::generateTooltip(FileInfo* info) const ++{ ++ if (!info) { ++ return QString(); ++ } ++ ++ if (isSystemFile(info)) { ++ return info->displayName(); ++ } ++ ++ if (info->fileType() == tr("Unknow")) { ++ return toNormalFile(info); ++ } ++ ++ if (info->isImageFile()) { ++ return toImage(info); ++ } ++ else if (info->isVideoFile() || info->isAudioFile()) { ++ return toVideoAudio(info); ++ } ++ else { ++ return toNormalFile(info); ++ } ++} ++ ++QString TooltipsManager::getMediaDuration(const QString& filePath, int timeoutMs) ++{ ++ QFileInfo fileInfo(filePath); ++ if (!fileInfo.exists()) { ++ qWarning() << "File does not exist:" << filePath; ++ return "N/A"; ++ } ++ ++ QProcess process; ++ QStringList arguments; ++ arguments << "-v" << "error" ++ << "-show_entries" << "format=duration" ++ << "-of" << "default=noprint_wrappers=1:nokey=1" ++ << "-sexagesimal" ++ << filePath; ++ ++ process.start("ffprobe", arguments); ++ ++ if (!process.waitForFinished(timeoutMs)) { ++ process.kill(); ++ qWarning() << "FFprobe process timed out for file:" << filePath; ++ return "N/A"; ++ } ++ ++ if (process.exitCode() != 0) { ++ qWarning() << "FFprobe process failed:" << process.errorString(); ++ return "N/A"; ++ } ++ ++ QString output = process.readAllStandardOutput().trimmed(); ++ QStringList parts = output.split("."); ++ ++ if (parts.isEmpty()) { ++ return "N/A"; ++ } ++ ++ // Get the main time part (excluding milliseconds) ++ QString mainPart = parts[0]; ++ ++ // Ensure format is HH:MM:SS ++ QStringList timeParts = mainPart.split(":"); ++ while (timeParts.size() < 3) { ++ timeParts.prepend("00"); ++ } ++ ++ // Combine time string, ensuring each part is two digits ++ return QString("%1:%2:%3") ++ .arg(timeParts[0].rightJustified(2, '0')) ++ .arg(timeParts[1].rightJustified(2, '0')) ++ .arg(timeParts[2].rightJustified(2, '0')); ++} ++ ++bool TooltipsManager::isSystemFile(FileInfo* info) ++{ ++ static const QString homeUri = "file://" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation); ++ static const QSet<QString> systemUris = { ++ "trash:///", "computer:///", "network:///", "recent:///", homeUri ++ }; ++ ++ bool isSystemFile = systemUris.contains(info->uri()); ++ if (!isSystemFile && !info->targetUri().isEmpty()) { ++ isSystemFile = systemUris.contains(info->targetUri()); ++ } ++ return isSystemFile; ++} ++ ++QString TooltipsManager::toVideoAudio(FileInfo* info) const ++{ ++ QString typeLbl = tr("Type: "); ++ QString fileType = info->fileType(); ++ ++ QUrl url = info->uri(); ++ QString duration = getMediaDuration(url.path()); ++ QString durationLbl = tr("Duration:"); ++ ++ QString sizeLbl = tr("Size: "); ++ bool isElided = info->property("isElided").toBool(); ++ ++ if (isElided) { ++ QString displayName = getDisplayName(info).toHtmlEscaped(); ++ return QString(CONTENT_TEMPLATE_WITH_FULLNAME).arg( ++ displayName, ++ typeLbl, fileType, ++ durationLbl, duration, ++ sizeLbl, info->fileSize() ++ ); ++ } else { ++ return QString(CONTENT_TEMPLATE_WITHOUT_FULLNAME).arg( ++ typeLbl, fileType, ++ durationLbl, duration, ++ sizeLbl, info->fileSize() ++ ); ++ } ++} ++ ++QString TooltipsManager::toImage(FileInfo* info) const ++{ ++ QString typeLbl = tr("Type: "); ++ QString fileType = info->fileType(); ++ ++ QUrl url = info->uri(); ++ QImageReader r(url.path()); ++ QString resolution = QString("%1x%2").arg(r.size().width()).arg(r.size().height()); ++ QString resolutionLbl = tr("Res: "); ++ ++ QString sizeLbl = tr("Size: "); ++ bool isElided = info->property("isElided").toBool(); ++ ++ if (isElided) { ++ QString displayName = getDisplayName(info).toHtmlEscaped(); ++ return QString(CONTENT_TEMPLATE_WITH_FULLNAME).arg( ++ displayName, ++ typeLbl, fileType, ++ resolutionLbl, resolution, ++ sizeLbl, info->fileSize() ++ ); ++ } else { ++ return QString(CONTENT_TEMPLATE_WITHOUT_FULLNAME).arg( ++ typeLbl, fileType, ++ resolutionLbl, resolution, ++ sizeLbl, info->fileSize() ++ ); ++ } ++} ++ ++QString TooltipsManager::toNormalFile(FileInfo* info) const ++{ ++ QString typeLbl = tr("Type: "); ++ QString typeUnKnow = tr("Unknow"); ++ QString fileType = info->fileType().isEmpty() ? typeUnKnow : info->fileType(); ++ ++ QString modDateLbl = tr("Mod Date: "); ++ QString sizeLbl = tr("Size: "); ++ bool isElided = info->property("isElided").toBool(); ++ ++ if (isElided) { ++ QString displayName = getDisplayName(info).toHtmlEscaped(); ++ return QString(CONTENT_TEMPLATE_WITH_FULLNAME).arg( ++ displayName, ++ typeLbl, fileType, ++ modDateLbl, info->modifiedDate(), ++ sizeLbl, info->fileSize() ++ ); ++ } else { ++ return QString(CONTENT_TEMPLATE_WITHOUT_FULLNAME).arg( ++ typeLbl, fileType, ++ modDateLbl, info->modifiedDate(), ++ sizeLbl, info->fileSize() ++ ); ++ } ++} ++ ++QString TooltipsManager::getDisplayName(FileInfo* info) const ++{ ++ QString displayName = info->displayName(); ++ ++ auto settings = GlobalSettings::getInstance(); ++ bool showFileExtension = settings->isExist(SHOW_FILE_EXTENSION) ? ++ settings->getValue(SHOW_FILE_EXTENSION).toBool() : ++ true; ++ ++ if (showFileExtension || info->isDir() || !displayName.contains(".")) { ++ return displayName; ++ } ++ ++ return FileUtils::getBaseNameOfFile(displayName); ++} ++ ++} // namespace Peony +diff --git a/libpeony-qt/tooltips-manager.h b/libpeony-qt/tooltips-manager.h +new file mode 100644 +index 0000000..e527464 +--- /dev/null ++++ b/libpeony-qt/tooltips-manager.h +@@ -0,0 +1,109 @@ ++/* ++ * Peony-Qt ++ * ++ * Copyright (C) 2024, Tianjin KYLIN Information Technology Co., Ltd. ++ * ++ * 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 3 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, see <https://www.gnu.org/licenses/>. ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-09-23 14:55 ++ */ ++ ++#ifndef TOOLTIPSMANAGER_H ++#define TOOLTIPSMANAGER_H ++ ++#include <QObject> ++#include "peony-core_global.h" ++ ++namespace Peony { ++class FileInfo; ++ ++/** ++ * @class TooltipsManager ++ * @brief Manages the generation of tooltips for file information. ++ * ++ * This class is responsible for creating tooltips with relevant information ++ * for different types of files, including images, videos, audio, and regular files. ++ */ ++class PEONYCORESHARED_EXPORT TooltipsManager: QObject ++{ ++ Q_OBJECT ++ Q_DISABLE_COPY(TooltipsManager) ++public: ++ /** ++ * @brief Get the singleton instance of TooltipsManager. ++ * @return Reference to the TooltipsManager instance. ++ */ ++ static TooltipsManager& instance(); ++ ++ /** ++ * @brief Generate a tooltip for the given file information. ++ * @param info Pointer to the FileInfo object. ++ * @return QString containing the generated tooltip HTML. ++ */ ++ QString generateTooltip(FileInfo* info) const; ++ ++ /** ++ * @brief Get the duration of a media file. ++ * @param filePath Path to the media file. ++ * @param timeoutMs Timeout for the ffprobe process in milliseconds. ++ * @return QString containing the duration in HH:MM:SS format. ++ */ ++ static QString getMediaDuration(const QString &filePath, int timeoutMs = 3000); ++ ++ /** ++ * @brief Check if the given file is a system file. ++ * @param info Pointer to the FileInfo object. ++ * @return true if it's a system file, false otherwise. ++ */ ++ static bool isSystemFile(FileInfo* info); ++ ++private: ++ TooltipsManager() = default; ++ ~TooltipsManager() = default; ++ ++ /** ++ * @brief Generate tooltip for video and audio files. ++ * @param info Pointer to the FileInfo object. ++ * @return QString containing the generated tooltip HTML. ++ */ ++ QString toVideoAudio(FileInfo* info) const; ++ ++ /** ++ * @brief Generate tooltip for image files. ++ * @param info Pointer to the FileInfo object. ++ * @return QString containing the generated tooltip HTML. ++ */ ++ QString toImage(FileInfo* info) const; ++ ++ /** ++ * @brief Generate tooltip for normal files. ++ * @param info Pointer to the FileInfo object. ++ * @return QString containing the generated tooltip HTML. ++ */ ++ QString toNormalFile(FileInfo* info) const; ++ ++ /** ++ * @brief Get the display name for a file. ++ * @param info Pointer to the FileInfo object. ++ * @return QString containing the display name. ++ */ ++ QString getDisplayName(FileInfo* info) const; ++}; ++ ++} // namespace Peony ++ ++#define TooltipsManagerInstance Peony::TooltipsManager::instance() ++ ++#endif // TOOLTIPSMANAGER_H +diff --git a/libpeony-qt/usershare-manager.cpp b/libpeony-qt/usershare-manager.cpp +index 3c65d4c..135672b 100644 +--- a/libpeony-qt/usershare-manager.cpp ++++ b/libpeony-qt/usershare-manager.cpp +@@ -425,6 +425,22 @@ UserShareInfoManager::UserShareInfoManager(QObject *parent) : QObject(parent) + QString name = Peony::FileUtils::urlDecode(uri).split("/").last(); + if (!name.contains(":") && m_usersharelists.contains(name)) { + m_usersharelists.removeOne(name); ++ m_mutex.lock(); ++ if (m_sharedInfoMap.contains(name)) { ++ if (nullptr != m_sharedInfoMap[name]) ++ { ++ delete m_sharedInfoMap[name]; ++ } ++ m_sharedInfoMap.remove(name); ++ } ++ if (m_usershareAclMap.contains(name)) { ++ if (!m_usershareAclMap[name].isEmpty()) ++ { ++ m_usershareAclMap.remove(name); ++ } ++ } ++ m_mutex.unlock(); ++ Q_EMIT signal_deleteUserShareList(name); + } + }); + +diff --git a/libpeony-qt/usershare-manager.h b/libpeony-qt/usershare-manager.h +index c6592e0..4f32e3d 100644 +--- a/libpeony-qt/usershare-manager.h ++++ b/libpeony-qt/usershare-manager.h +@@ -76,6 +76,7 @@ private: + Q_SIGNALS: + void signal_addSharedFolder(const ShareInfo& shareInfo, bool successed); + void signal_deleteSharedFolder(const QString& originalPath, bool successed); ++ void signal_deleteUserShareList(const QString& name); + + private: + bool m_bInit = false; +diff --git a/libpeony-qt/vfs/favorite-vfs-file-enumerator.cpp b/libpeony-qt/vfs/favorite-vfs-file-enumerator.cpp +index 0b71bbc..6404be9 100644 +--- a/libpeony-qt/vfs/favorite-vfs-file-enumerator.cpp ++++ b/libpeony-qt/vfs/favorite-vfs-file-enumerator.cpp +@@ -63,6 +63,7 @@ static void vfs_favorites_file_enumerator_init (FavoritesVFSFileEnumerator* self + //self->priv->enumerate_queue->enqueue(QString("favorite://%1?schema=file").arg(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation))); + //self->priv->enumerate_queue->enqueue(QString("favorite://%1?schema=file").arg(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation))); + ++ //fix bug#228435, show usershare issue + if (Peony::FileUtils::isFileExsit("file:///data/usershare")) { + self->priv->enumerate_queue->enqueue("favorite:///data/usershare?schema=file"); + } +diff --git a/libpeony-qt/vfs/label-vfs-file-monitor.cpp b/libpeony-qt/vfs/label-vfs-file-monitor.cpp +index 800a215..93e7266 100644 +--- a/libpeony-qt/vfs/label-vfs-file-monitor.cpp ++++ b/libpeony-qt/vfs/label-vfs-file-monitor.cpp +@@ -54,6 +54,7 @@ static void vfs_label_file_monitor_dispose (GObject* obj) + + QObject::disconnect(self->add); + QObject::disconnect(self->remove); ++ QObject::disconnect(self->dirAttrChanged); + } + + static void vfs_label_file_monitor_finalize (GObject* obj) +@@ -97,3 +98,17 @@ void vfs_label_file_monitor_dir(LabelVFSFileMonitor *obj, const QString &label_v + } + }); + } ++ ++void vfs_label_file_monitor_file(LabelVFSFileMonitor *obj, const QString &label_vfs_directory_uri) ++{ ++ g_return_if_fail(VFS_IS_LABEL_FILE_MONITOR(obj)); ++ ++ FileLabelModel* fileLabel = FileLabelModel::getGlobalModel(); ++ obj->dirAttrChanged = QObject::connect(fileLabel, &FileLabelModel::labelColorNameChanged, [=] (const QString uri, const QString& oldColorName, const QString& newColorName) { ++ if (oldColorName != newColorName && uri.startsWith(label_vfs_directory_uri)) { ++ GFile* file = g_file_new_for_uri(label_vfs_directory_uri.toUtf8().constData()); ++ g_file_monitor_emit_event(G_FILE_MONITOR(obj), file, nullptr, G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED); ++ vfs_label_file_monitor_free_gfile (VFS_LABEL_FILE_MONITOR(obj), G_FILE(file)); ++ } ++ }); ++} +diff --git a/libpeony-qt/vfs/label-vfs-file-monitor.h b/libpeony-qt/vfs/label-vfs-file-monitor.h +index f45069e..73b075c 100644 +--- a/libpeony-qt/vfs/label-vfs-file-monitor.h ++++ b/libpeony-qt/vfs/label-vfs-file-monitor.h +@@ -57,6 +57,8 @@ struct LabelVFSFileMonitor + GList *fileList = nullptr; + QMetaObject::Connection add; + QMetaObject::Connection remove; ++ QMetaObject::Connection dirAttrChanged; ++ + }; + + struct LabelVFSFileMonitorClass +@@ -67,6 +69,7 @@ struct LabelVFSFileMonitorClass + + GType vfs_label_file_monitor_get_type (void) G_GNUC_CONST; + void vfs_label_file_monitor_dir(LabelVFSFileMonitor *obj, const QString &label_vfs_directory_uri); ++void vfs_label_file_monitor_file(LabelVFSFileMonitor *obj, const QString &label_vfs_directory_uri); + + + G_END_DECLS +diff --git a/libpeony-qt/vfs/label-vfs-file.cpp b/libpeony-qt/vfs/label-vfs-file.cpp +index 30163bc..3989aee 100644 +--- a/libpeony-qt/vfs/label-vfs-file.cpp ++++ b/libpeony-qt/vfs/label-vfs-file.cpp +@@ -50,6 +50,7 @@ GFileIOStream * vfs_label_file_open_readwrite(GFile* file, GCancellable* can + GMount * vfs_label_file_find_enclosing_mount(GFile* file, GCancellable* cancellable, GError** error); + gboolean vfs_label_file_make_symbolic_link(GFile* file, const char* svalue, GCancellable* cancellable, GError** error); + GFileMonitor* vfs_label_file_monitor_directory (GFile* file, GFileMonitorFlags flags, GCancellable* cancellable, GError** error); ++GFileMonitor* vfs_label_file_monitor_file (GFile* file, GFileMonitorFlags flags, GCancellable* cancellable, GError** error); + GFile* vfs_label_file_set_display_name (GFile* file, const gchar* display_name, GCancellable* cancellable, GError** error); + GFileInfo* vfs_label_file_query_filesystem_info(GFile* file, const char* attributes, GCancellable* cancellable, GError** error); + GFileInfo* vfs_label_file_query_info(GFile *file, const char *attributes, GFileQueryInfoFlags flags, GCancellable *cancellable, GError **error); +@@ -94,6 +95,7 @@ static void vfs_label_file_g_file_iface_init(GFileIface *iface) + iface->set_display_name = vfs_label_file_set_display_name; + iface->get_relative_path = vfs_label_file_get_relative_path; + iface->monitor_dir = vfs_label_file_monitor_directory; ++ iface->monitor_file = vfs_label_file_monitor_file; + iface->make_symbolic_link = vfs_label_file_make_symbolic_link; + iface->enumerate_children = vfs_label_file_enumerate_children; + iface->find_enclosing_mount = vfs_label_file_find_enclosing_mount; +@@ -258,7 +260,7 @@ gboolean vfs_label_file_delete (GFile* file, GCancellable* cancellable, GError** + QUrl url(uri); + QString realUri = QString("file:///").append(url.path().section("/", 2,-1)); + QString encodeUri = Peony::FileUtils::getEncodedUri(realUri); +- int labelId = FileLabelModel::getGlobalModel()->getLabelIdFromLabelName(url.path().section("/", 1, 1)); ++ int labelId = url.path().section("/", 1, 1).toInt(); + FileLabelModel::getGlobalModel()->removeFileLabel(encodeUri, labelId); + return TRUE; + } +@@ -303,6 +305,24 @@ GFileMonitor* vfs_label_file_monitor_directory (GFile* file, GFileMonitorFlags f + return priv->fileMonitor; + } + ++GFileMonitor* vfs_label_file_monitor_file (GFile* file, GFileMonitorFlags flags, GCancellable* cancellable, GError** error){ ++ ++ g_return_val_if_fail(VFS_IS_LABEL_FILE(file), nullptr); ++ ++ LabelVFSFilePrivate* priv = VFS_LABEL_FILE((LabelVFSFile*)file)->priv; ++ priv->fileMonitor = (GFileMonitor*)g_object_new (VFS_TYPE_LABEL_FILE_MONITOR, nullptr); ++ QString uri = g_file_get_uri(file); ++ vfs_label_file_monitor_file(VFS_LABEL_FILE_MONITOR(priv->fileMonitor), uri); ++ ++ ++ Q_UNUSED(file) ++ Q_UNUSED(flags) ++ Q_UNUSED(error) ++ Q_UNUSED(cancellable) ++ ++ return priv->fileMonitor; ++} ++ + GFile* vfs_label_file_set_display_name (GFile* file, const gchar* display_name, GCancellable* cancellable, GError** error){ + Q_UNUSED(error); + Q_UNUSED(cancellable); +@@ -346,6 +366,9 @@ GFileInfo* vfs_label_file_query_info(GFile *file, const char *attributes, GFileQ + auto icon = g_themed_icon_new("label"); + g_file_info_set_icon(info, icon); + g_object_unref(icon); ++ }else if(!(url.toString().section("label:///", -1,-1).contains("/"))){ ++ QString labelIdStr = url.toString().section("label:///", -1,-1); ++ name = FileLabelModel::getGlobalModel()->getLabelNameFromLabelId(labelIdStr.toInt()); + }else{ + name = url.toString().section("/", -1,-1); + } +@@ -467,8 +490,7 @@ gboolean vfs_label_file_is_exist(const char *uri) + void label_vfs_file_enumerator_parse_uri(LabelVFSFileEnumerator *enumerator, const QString& uri){ + LabelVFSFileEnumeratorPrivate *priv = enumerator->priv; + *priv->label_vfs_directory_uri = uri; +- auto tmp = priv->label_vfs_directory_uri->section("/", -1,-1); +- int labelId = FileLabelModel::getGlobalModel()->getLabelIdFromLabelName(tmp); ++ auto labelId = priv->label_vfs_directory_uri->section("/", -1,-1).toInt(); + QSet<QString> uriSet = FileLabelModel::getGlobalModel()->getFileUrisFromLabelId(labelId); + for(const QString &str: uriSet){ + QUrl url = QUrl(str); +diff --git a/libpeony-qt/vfs/peony-search-vfs-file-enumerator.cpp b/libpeony-qt/vfs/peony-search-vfs-file-enumerator.cpp +index bacc75f..701a471 100644 +--- a/libpeony-qt/vfs/peony-search-vfs-file-enumerator.cpp ++++ b/libpeony-qt/vfs/peony-search-vfs-file-enumerator.cpp +@@ -92,8 +92,12 @@ static void peony_search_vfs_file_enumerator_init(PeonySearchVFSFileEnumerator * + #ifdef KY_UKUI_SEARCH + self->priv->m_search = new UkuiSearch::UkuiSearchTask(); + self->priv->m_queue = self->priv->m_search->init(); ++ self->priv->m_contentSearch = new UkuiSearch::UkuiSearchTask(); ++ self->priv->m_contentQueue = self->priv->m_contentSearch->init(); + self->priv->search_engine = false; +- self->priv->search_first = true; ++ self->priv->search_status = SEARCHFILE; ++ self->priv->search_content = SEARCHFILECONTENT; ++ self->priv->m_duplicatesHash = new QHash<QString, QString>; + #endif + } + +@@ -159,6 +163,12 @@ void enumerator_dispose(GObject *object) + self->priv->m_search->stop(); + } + delete self->priv->m_search; ++ if (self->priv->m_contentSearch->isSearching(UkuiSearch::SearchProperty::SearchType::FileContent)) { ++ self->priv->m_contentSearch->stop(); ++ } ++ delete self->priv->m_contentSearch; ++ self->priv->m_duplicatesHash->clear(); ++ delete self->priv->m_duplicatesHash; + #endif + } + +@@ -199,14 +209,18 @@ static GFileInfo *enumerate_next_file(GFileEnumerator *enumerator, + bool search_engine = false; + #ifdef KY_UKUI_SEARCH + search_engine = search_enumerator->priv->search_engine; +- if (search_enumerator->priv->search_first) { ++ if (search_enumerator->priv->search_status == SEARCHFILE) { + search_enumerator->priv->m_search->startSearch(UkuiSearch::SearchProperty::SearchType::File); +- search_enumerator->priv->search_first = false; ++ search_enumerator->priv->search_status = SEARCHFILEING; ++ qDebug() << __func__ << __LINE__ << "start file name search"; + } + + while (true) { + if (!search_enumerator->priv->m_search->isSearching(UkuiSearch::SearchProperty::SearchType::File) + || !search_enumerator->priv->m_queue->isEmpty()) { ++ if (!search_enumerator->priv->m_search->isSearching(UkuiSearch::SearchProperty::SearchType::File) ++ && search_enumerator->priv->search_status == SEARCHFILEING) { ++ } + break; + } + } +@@ -218,6 +232,7 @@ static GFileInfo *enumerate_next_file(GFileEnumerator *enumerator, + g_autofree gchar* encoded_path = g_uri_escape_string(path.toUtf8().constData(), ":/", false); + path = encoded_path; + QString uri = "file://" + path; ++ search_enumerator->priv->m_duplicatesHash->insert(uri, "file"); + auto search_vfs_info = g_file_info_new(); + QString realUriSuffix = "real-uri:" + uri; + g_file_info_set_name(search_vfs_info, realUriSuffix.toUtf8().constData()); +@@ -230,6 +245,42 @@ static GFileInfo *enumerate_next_file(GFileEnumerator *enumerator, + //search_enumerator->priv->m_count++; + return search_vfs_info; + } ++ ++ ++ if (search_enumerator->priv->search_content == SEARCHFILECONTENT) { ++ search_enumerator->priv->m_contentSearch->startSearch(UkuiSearch::SearchProperty::SearchType::FileContent); ++ search_enumerator->priv->search_content = SEARCHFILECONTENTING; ++ qDebug() << __func__ << __LINE__ << "start file content search"; ++ } ++ ++ while (true) { ++ if (!search_enumerator->priv->m_contentSearch->isSearching(UkuiSearch::SearchProperty::SearchType::FileContent) ++ || !search_enumerator->priv->m_contentQueue->isEmpty()) { ++ break; ++ } ++ } ++ ++ while (!search_enumerator->priv->m_contentQueue->isEmpty() && search_engine) { ++ UkuiSearch::ResultItem resultItem = search_enumerator->priv->m_contentQueue->dequeue(); ++ //qDebug() << "resultItem-->" << resultItem.getItemKey(); ++ QString path = resultItem.getItemKey(); ++ g_autofree gchar* encoded_path = g_uri_escape_string(path.toUtf8().constData(), ":/", false); ++ path = encoded_path; ++ QString uri = "file://" + path; ++ if (search_enumerator->priv->m_duplicatesHash->contains(uri)) { ++ continue; ++ } ++ search_enumerator->priv->m_duplicatesHash->insert(uri, "fileContent"); ++ auto search_vfs_info = g_file_info_new(); ++ QString realUriSuffix = "real-uri:" + uri; ++ g_file_info_set_name(search_vfs_info, realUriSuffix.toUtf8().constData()); ++ ++ if (search_enumerator->priv->save_result) { ++ auto historyResults = manager->getHistroyResults(*search_enumerator->priv->search_vfs_directory_uri); ++ historyResults<<realUriSuffix; ++ } ++ return search_vfs_info; ++ } + #endif + + while (!enumerate_queue->isEmpty() && !search_engine) { +diff --git a/libpeony-qt/vfs/peony-search-vfs-file-enumerator.h b/libpeony-qt/vfs/peony-search-vfs-file-enumerator.h +index bc266f5..10995ec 100644 +--- a/libpeony-qt/vfs/peony-search-vfs-file-enumerator.h ++++ b/libpeony-qt/vfs/peony-search-vfs-file-enumerator.h +@@ -45,6 +45,13 @@ G_DECLARE_FINAL_TYPE(PeonySearchVFSFileEnumerator, + + PeonySearchVFSFileEnumerator *peony_search_vfs_file_enumerator_new(void); + ++enum PeonySearchStatus { ++ SEARCHFILE, ++ SEARCHFILECONTENT, ++ SEARCHFILEING, ++ SEARCHFILECONTENTING ++}; ++ + typedef struct { + QString *search_vfs_directory_uri; + /*! +@@ -64,8 +71,12 @@ typedef struct { + #ifdef KY_UKUI_SEARCH + UkuiSearch::UkuiSearchTask *m_search; + UkuiSearch::DataQueue<UkuiSearch::ResultItem> *m_queue; ++ UkuiSearch::UkuiSearchTask *m_contentSearch; ++ UkuiSearch::DataQueue<UkuiSearch::ResultItem> *m_contentQueue; + gboolean search_engine; +- gboolean search_first; ++ PeonySearchStatus search_status; ++ PeonySearchStatus search_content; ++ QHash<QString, QString> *m_duplicatesHash; + #endif + } PeonySearchVFSFileEnumeratorPrivate; + +diff --git a/libpeony-qt/vfs/peony-search-vfs-file.cpp b/libpeony-qt/vfs/peony-search-vfs-file.cpp +index c6d1279..1fded31 100644 +--- a/libpeony-qt/vfs/peony-search-vfs-file.cpp ++++ b/libpeony-qt/vfs/peony-search-vfs-file.cpp +@@ -215,7 +215,7 @@ void peony_search_vfs_file_enumerator_parse_uri(PeonySearchVFSFileEnumerator *en + QStringList paths; + QStringList keyWords; + +- if (args.at(1).contains("name_regexp=") && 12 == args.at(1).size() ++ if (args.size() > 1 && args.at(1).contains("name_regexp=") && 12 == args.at(1).size() + && !details->search_vfs_directory_uri->contains("search_hidden=") + && !details->search_vfs_directory_uri->contains("use_regexp=") + && !details->search_vfs_directory_uri->contains("case_sensitive=") +@@ -381,7 +381,6 @@ void peony_search_vfs_file_enumerator_parse_uri(PeonySearchVFSFileEnumerator *en + } + + details->m_search->initSearchPlugin(UkuiSearch::SearchProperty::SearchType::File); +- details->m_search->initSearchPlugin(UkuiSearch::SearchProperty::SearchType::FileContent); + details->m_queue = details->m_search->init(); + details->m_search->setMaxResultNum(9999999); + +@@ -396,6 +395,26 @@ void peony_search_vfs_file_enumerator_parse_uri(PeonySearchVFSFileEnumerator *en + // l->exec(); + // l->deleteLater(); + } ++ ++ if (nullptr != details->m_contentSearch && details->search_engine) { ++ details->m_contentSearch->clearAllConditions(); ++ details->m_contentQueue->clear(); ++ ++ for (QString &dir : paths) { ++ dir = Peony::FileUtils::urlDecode(dir); ++ details->m_contentSearch->addSearchDir(dir); ++ } ++ ++ for (QString &key : keyWords) { ++ details->m_contentSearch->addKeyword(key); ++ } ++ ++ details->m_contentSearch->initSearchPlugin(UkuiSearch::SearchProperty::SearchType::FileContent); ++ details->m_contentQueue = details->m_contentSearch->init(); ++ details->m_contentSearch->setMaxResultNum(9999999); ++ } ++ ++ details->m_duplicatesHash->clear(); + #endif + } + +diff --git a/libpeony-qt/vfs/recent-vfs-manager.cpp b/libpeony-qt/vfs/recent-vfs-manager.cpp +index 8eed990..ae725ae 100644 +--- a/libpeony-qt/vfs/recent-vfs-manager.cpp ++++ b/libpeony-qt/vfs/recent-vfs-manager.cpp +@@ -147,6 +147,11 @@ bool RecentVFSManager::write() + return ok; + } + ++bool RecentVFSManager::exists(QString uri) ++{ ++ return false; ++} ++ + bool RecentVFSManager::exists(QString uri, QString mimetype, QString name, QString exec) + { + if (!read()) { +diff --git a/libpeony-qt/vfs/recent-vfs-manager.h b/libpeony-qt/vfs/recent-vfs-manager.h +index 2928838..e3eca2d 100644 +--- a/libpeony-qt/vfs/recent-vfs-manager.h ++++ b/libpeony-qt/vfs/recent-vfs-manager.h +@@ -40,6 +40,13 @@ private: + explicit RecentVFSManager(QObject *parent = nullptr); + bool read (); + bool write (); ++ /*! ++ * \brief exists ++ * \param uri ++ * \return ++ * \deprecated ++ */ ++ bool exists(QString uri); + bool exists (QString uri, QString mimetype, QString name, QString exec); + bool createNode (QString uri, QString mimetype, QString name, QString exec); + +diff --git a/libpeony-qt/vfs/search-vfs-uri-parser.cpp b/libpeony-qt/vfs/search-vfs-uri-parser.cpp +index bd82ab1..0a3c65f 100644 +--- a/libpeony-qt/vfs/search-vfs-uri-parser.cpp ++++ b/libpeony-qt/vfs/search-vfs-uri-parser.cpp +@@ -137,3 +137,18 @@ const QString SearchVFSUriParser::addSearchKey(const QString &uri, const bool &s + } + return search_str; + } ++ ++const QString SearchVFSUriParser::getSearchUriPath(const QString &searchUri) ++{ ++ auto string = searchUri; ++ string.remove("search:///"); ++ auto list = string.split("&"); ++ for (auto arg : list) { ++ if (arg.startsWith("search_uris=")) { ++ qDebug()<<"arg:"<<arg; ++ auto tmp = arg.remove("search_uris="); ++ return tmp; ++ } ++ } ++ return QString(); ++} +diff --git a/libpeony-qt/vfs/search-vfs-uri-parser.h b/libpeony-qt/vfs/search-vfs-uri-parser.h +index 56ace7f..148f694 100644 +--- a/libpeony-qt/vfs/search-vfs-uri-parser.h ++++ b/libpeony-qt/vfs/search-vfs-uri-parser.h +@@ -36,6 +36,7 @@ public: + const static QString getSearchUriNameRegexp(const QString &searchUri); + const static QString getSearchUriTargetDirectory(const QString &searchUri); + const static QString addSearchKey(const QString &uri, const bool &search_engine = false); ++ const static QString getSearchUriPath(const QString &searchUri); + private: + SearchVFSUriParser(); + }; +diff --git a/libpeony-qt/volumeManager.cpp b/libpeony-qt/volumeManager.cpp +index 4761358..5e07088 100644 +--- a/libpeony-qt/volumeManager.cpp ++++ b/libpeony-qt/volumeManager.cpp +@@ -209,7 +209,8 @@ VolumeManager::VolumeManager(QObject *parent) : QObject(parent) + if(0 == occupiedAppMap.size()){ + QTimer::singleShot(500,[=](){ + QMutexLocker lk(&m_mutex); +- m_occupiedVolume->eject(G_MOUNT_UNMOUNT_NONE); ++ if(m_occupiedVolume) ++ m_occupiedVolume->eject(G_MOUNT_UNMOUNT_NONE); + }); + //QMessageBox::critical(nullptr, QObject::tr("Eject failed"), message); + }else{ +@@ -218,6 +219,14 @@ VolumeManager::VolumeManager(QObject *parent) : QObject(parent) + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->exec(); + } ++ { ++ QMutexLocker lk(&m_mutex); ++ if(m_occupiedVolume){ ++ delete m_occupiedVolume; ++ m_occupiedVolume = nullptr; ++ m_occupiedVolumeUri = QString(); ++ } ++ } + }, Qt::QueuedConnection); + m_occupiedAppsInfoThread->start(); + } +@@ -252,6 +261,7 @@ VolumeManager::~VolumeManager(){ + if(m_occupiedVolume){ + delete m_occupiedVolume; + m_occupiedVolume = nullptr; ++ m_occupiedVolumeUri = QString(); + } + } + } +@@ -362,7 +372,7 @@ void VolumeManager::volumeAddCallback(GVolumeMonitor *monitor, + Q_EMIT pThis->volumeRemove(device); + pThis->m_volumeList->remove(device); + pThis->m_volumeList->insert(device, addItem); +- qDebug()<<__func__<<__LINE__<<device; ++ qDebug()<<__func__<<__LINE__<<device<<"isHidden:"<<addItem->getHidden(); + Q_EMIT pThis->volumeAdd(Volume(*addItem)); + //情景1、关闭gparted时,所有具有卸载属性的设备均会触发volume-added信号 + // 该情景似乎不需要更新属性信息,确认一下name属性? +@@ -387,7 +397,7 @@ void VolumeManager::volumeAddCallback(GVolumeMonitor *monitor, + //情景3、默认用数据线连接的手机("仅充电") + pThis->m_volumeList->remove(addItem->device()); + pThis->m_volumeList->insert(addItem->device(),addItem); +- qDebug()<<__func__<<__LINE__<<addItem->device(); ++ qDebug()<<__func__<<__LINE__<<addItem->device()<<addItem->getHidden(); + Q_EMIT pThis->volumeAdd(Volume(*addItem)); + } + } +@@ -430,13 +440,14 @@ void VolumeManager::volumeRemoveCallback(GVolumeMonitor *monitor, + if (device.startsWith("/dev/sd")) { + QString uuid = getDeviceUUID(device.toUtf8().constData()); + auto size = Peony::FileUtils::getDeviceSize(device.toUtf8().constData()); +- qDebug()<<__func__<<__LINE__<<uuid<<size; ++ qDebug()<<__func__<<__LINE__<<device<<"uuid:"<<uuid<<"size:"<<size; + if (uuid.isEmpty() && size == 0) { + addItem->setHidden(true); + // if drive has media, it is not represent a docking station. + // so it should not be hidden. + if (driveHasMedia(gdrive)) { + addItem->setHidden(false); ++ qDebug()<<device<<"set hidden false"; + } + } + }//end +@@ -469,7 +480,7 @@ void VolumeManager::volumeRemoveCallback(GVolumeMonitor *monitor, + QString device = gdevice; + g_free(gdevice); + +- //qDebug()<<__func__<<__LINE__<<device<<endl; ++ qDebug()<<__func__<<__LINE__<<device<<endl; + if(!pThis->m_volumeList->contains(device)){ + //情景3、已经发生了暴力拔出的情况,设备已经不存在了,这里不用做处理了 + //情景4、数据线连接的手机状态改变:"仅充电"->"传输文件(mtp)"或"传输图片(gphoto)" +@@ -481,7 +492,7 @@ void VolumeManager::volumeRemoveCallback(GVolumeMonitor *monitor, + blankCDFlag = device.contains("/dev/sr") && pThis->m_volumeList->value(device)->mountPoint().isEmpty(); + phoneOrCD = device.contains("/dev/bus") || device.contains("/dev/sr"); + Q_UNUSED(phoneOrCD) +- qDebug()<<__func__<<__LINE__<<device<<(gmount!=nullptr)<<endl; ++ qDebug()<<__func__<<__LINE__<<device<<"gmount not null:"<<(gmount!=nullptr)<<endl; + if(gmount){ + if(!phoneFlag && !blankCDFlag){ + //情景5、手机的mtp与gphoto2状态相互转换时只能保留一个 +@@ -491,7 +502,7 @@ void VolumeManager::volumeRemoveCallback(GVolumeMonitor *monitor, + //情景8、空光盘弹出后空光驱应该被移除 + //Q_EMIT pThis->volumeUpdate(); + } +- qDebug()<<__func__<<__LINE__<<device<<(gmount!=nullptr)<<endl; ++ qDebug()<<__func__<<__LINE__<<device<<"gmount not null:"<<(gmount!=nullptr)<<endl; + + if (blankCDFlag) + return; +@@ -527,7 +538,7 @@ void VolumeManager::mountRemoveCallback(GVolumeMonitor *monitor, + + QHash<QString,Volume*>::iterator item = pThis->m_volumeList->begin(); + QHash<QString,Volume*>::iterator end = pThis->m_volumeList->end(); +- //qDebug()<<__func__<<__LINE__<<mountPoint<<endl; ++ qDebug()<<__func__<<__LINE__<<"mountPoint:"<<mountPoint<<endl; + g_signal_connect(gmount, "changed", G_CALLBACK(mountChangedCallback),pThis);/* 监听mount的changed信号,获取mountPoint */ + //查看gparted进程是否存在,以便确定是否要移除设备 + pThis->gpartedIsOpening(); +@@ -552,7 +563,14 @@ void VolumeManager::mountRemoveCallback(GVolumeMonitor *monitor, + Q_EMIT pThis->mountRemove(mountPoint); + } + +- ++ { ++ QMutexLocker lk(&pThis->m_mutex); ++ if(pThis->m_occupiedVolume && volumeItem && volumeItem->device() == pThis->m_occupiedVolume->device()){ ++ delete pThis->m_occupiedVolume; ++ pThis->m_occupiedVolume = nullptr; ++ pThis->m_occupiedVolumeUri = QString(); ++ } ++ } + delete mountItem; + } + +@@ -657,7 +675,8 @@ void VolumeManager::mountPreUnmountCallback(GVolumeMonitor *monitor, GMount *gmo + { + QMutexLocker lk(pThis->getMutex()); + pThis->m_occupiedVolume = volume; +- qDebug()<<"mount pre-unmount: "<<volume->device()<<pThis->m_occupiedVolume; ++ pThis->m_occupiedVolumeUri = Experimental_Peony::VolumeManager::getInstance()->getTargetUriFromUnixDevice(volume->device()); ++ qDebug()<<"mount pre-unmount: "<<volume->device()<<pThis->m_occupiedVolume<<pThis->m_occupiedVolumeUri ; + } + } + } +@@ -697,6 +716,12 @@ void VolumeManager::driveConnectCallback(GVolumeMonitor *monitor, + // so it should not be hidden. + if (driveHasMedia(gdrive)) { + volume->setHidden(false); ++ qDebug() << "uuid=0 && size=0 but has media, show volume:"<<volume->device(); ++ } ++ }else if(uuid.isEmpty() && size != 0 && gdrive){/* hotfix bug#274521 【浪潮计算机】【CE520L2】红盘识别异常问题 */ ++ qDebug()<<__func__<<__LINE__<<volume->device()<<"the icon of volume"<<volume->icon()<<"can-stop:"<<volume->canStop()<<"isHidden:"<<volume->getHidden(); ++ if(!volume->canStop()){ ++ volume->setHidden(true); + } + } + } +@@ -746,6 +771,7 @@ void VolumeManager::driveDisconnectCallback(GVolumeMonitor *monitor, + if(pThis->m_occupiedVolume){ + delete pThis->m_occupiedVolume; + pThis->m_occupiedVolume = nullptr; ++ pThis->m_occupiedVolumeUri = QString(); + } + } + } +@@ -853,7 +879,7 @@ QList<Volume>* VolumeManager::allVaildVolumes(){ + for(int i=0; i<mountCount; ++i){ + Volume* volumeItem = new Volume(nullptr); + volumeItem->setFromMount(*mounts.at(i));//从Mount对象构造Volume对象数据 +- //qDebug()<<__func__<<__LINE__<<volumeItem->device()<<volumeItem->name(); ++ qDebug()<<__func__<<__LINE__<<volumeItem->device()<<volumeItem->name(); + m_volumeList->remove(volumeItem->device()); + m_volumeList->insert(volumeItem->device(),volumeItem); + delete mounts.at(i); +@@ -863,12 +889,12 @@ QList<Volume>* VolumeManager::allVaildVolumes(){ + if(!m_gpartedIsOpening){ //gparted未打开时,才考虑卷设备未挂载的情况 + for(int i=0; i<volumeCount; ++i){ + Volume* volumeItem = volumes.at(i); +- //qDebug()<<__func__<<__LINE__<<volumeItem->device()<<endl; ++ qDebug()<<__func__<<__LINE__<<volumeItem->device()<<endl; + if(m_volumeList->contains(volumeItem->device())) { + delete volumeItem; + continue; + } +- //qDebug()<<__func__<<__LINE__<<volumeItem->device()<<volumeItem->name(); ++ qDebug()<<__func__<<__LINE__<<volumeItem->device()<<volumeItem->name(); + auto oldVolume = m_volumeList->take(volumeItem->device()); + delete oldVolume; + m_volumeList->insert(volumeItem->device(),volumeItem); +@@ -931,17 +957,18 @@ QList<Volume>* VolumeManager::allVaildVolumes(){ + if (entry->getGDrive()) { + if (driveHasMedia(entry->getGDrive())) { + volumeItem->setHidden(false); ++ qDebug() << "uuid=0 && size=0 but has gdrive, show volume:"<<volumeItem->device(); + } + } +- } +- else if(uuid.isEmpty() && size != 0 && entry->getGDrive()){ +- qDebug()<<"the icon of volume"<<volumeItem->device()<<volumeItem->icon(); +- if("drive-removable-media" == volumeItem->icon()){/* 由此判断区分本地固态硬盘(SATA、SSD等)和异常U盘 */ ++ }else if(uuid.isEmpty() && size != 0 && entry->getGDrive()){ ++ qDebug()<<__func__<<__LINE__<<volumeItem->device()<<"the icon of volume:"<<volumeItem->icon()<<"can-stop:"<<volumeItem->canStop()<<"isHidden:"<<volumeItem->getHidden()<<"hasVolume:"<<bHasVolume; ++ if("drive-removable-media" == volumeItem->icon() || !volumeItem->canStop()){/* 由此判断区分本地固态硬盘(SATA、SSD等)和异常U盘 */ + //fix show SATA, SSD unparted device /dev/sda issue, link to bug#135269,125009,206525 + volumeItem->setHidden(true); + } + } + } ++ qDebug()<<__func__<<__LINE__<<volumeItem->device()<<"isHidden:"<<volumeItem->getHidden()<<",hasVolume:"<<bHasVolume; + if(bHasVolume){/* 解决:U盘多个分区时,侧边栏会显示drive */ + volumeItem->setHidden(true); + } +@@ -1342,7 +1369,7 @@ Volume* Volume::initRootVolume(){ + m_canEject = false; + m_canStop = false; + m_volume = nullptr; +- m_name = "File System"; ++ m_name = "System Disk"; + + GUnixMountEntry* entry = g_unix_mount_at("/",nullptr); + if(!entry) +@@ -1624,27 +1651,30 @@ void Mount::initMountInfo(){ + } + + void Mount::queryDeviceByMountpoint(){ +- const char* device; +- char* mountPoint; + if(m_mountPoint.isEmpty()) + return; + + //处理uri转码 + if(m_mountPoint.startsWith("file:///")){ +- mountPoint = g_filename_from_uri(m_mountPoint.toUtf8().constData(),nullptr,nullptr); ++ char* mountPoint = g_filename_from_uri(m_mountPoint.toUtf8().constData(),nullptr,nullptr); + m_mountPoint = mountPoint; + g_free(mountPoint); + } +- //mountPoint = m_mountPoint.toUtf8().constData(); +- //qDebug()<<__func__<<__LINE__<<m_mountPoint<<endl; +- m_entry = g_unix_mount_at(m_mountPoint.toUtf8().constData(),nullptr); +- if(!m_entry) +- m_entry = g_unix_mount_for(m_mountPoint.toUtf8().constData(),nullptr); +- if(!m_entry) ++ ++ qDebug()<<__func__<<__LINE__<<m_mountPoint<<endl; ++ GUnixMountEntry* entry = g_unix_mount_at(m_mountPoint.toUtf8().constData(),nullptr); ++ if(!entry) ++ entry = g_unix_mount_for(m_mountPoint.toUtf8().constData(),nullptr); ++ if(!entry) + return; +- //qDebug()<<__func__<<__LINE__<<m_mountPoint<<endl; +- device = g_unix_mount_get_device_path(m_entry); +- m_device = device; ++ ++ if(g_unix_mount_get_mount_path(entry) == m_mountPoint){ ++ /* GUnixMountEntry 获取的mountpath与m_mountPoint相同时,由GUnixMountEntry获取到的device才正确;linkto bug#226673 */ ++ const char* device = g_unix_mount_get_device_path(entry); ++ m_device = device; ++ g_unix_mount_free(entry); ++ qDebug()<<__func__<<__LINE__<<m_mountPoint<<m_device<<endl; ++ } + } + + /*==================Volume property==============*/ +@@ -1943,7 +1973,6 @@ void GetOccupiedAppsInfoThread::show_processes_cb(GMountOperation *MountOp, char + + auto thread = static_cast<GetOccupiedAppsInfoThread *>(user_data); + thread->signal_occupiedAppInfo(occupiedAppMap, message); +- g_mount_operation_reply(thread->getMountOp(), G_MOUNT_OPERATION_ABORTED);/* 解决一直弹出信息框问题,link to issue#I9VOWE. */ + } + + GMountOperation *GetOccupiedAppsInfoThread::getMountOp() const +diff --git a/libpeony-qt/volumeManager.h b/libpeony-qt/volumeManager.h +index 62f96d7..1146423 100644 +--- a/libpeony-qt/volumeManager.h ++++ b/libpeony-qt/volumeManager.h +@@ -65,6 +65,9 @@ public: + Volume* getOccupiedVolume(){ + return m_occupiedVolume; + } ++ QString getOccupiedVolumeUri(){ ++ return m_occupiedVolumeUri; ++ } + QMutex* getMutex(){ + return &m_mutex; + }; +@@ -113,6 +116,7 @@ private: + QHash<QString,Volume*>* m_volumeList = nullptr; + GetOccupiedAppsInfoThread* m_occupiedAppsInfoThread = nullptr; + Volume *m_occupiedVolume = nullptr; /* 被占用的volume,当前用于只有ffmpeg占用时强制弹出,link to bug#117263 */ ++ QString m_occupiedVolumeUri; + QMutex m_mutex; + + //我应该在检测到信号时更新卷设备列表?还是在用到时重新全部get一次?感觉前者好点? +@@ -182,7 +186,8 @@ private: + QString m_icon; + QString m_device; + QString m_mountPoint; +- GUnixMountEntry * m_entry = nullptr; ++ GUnixMountEntry *m_entry = nullptr; ++ + private: + void initMountInfo(); + void queryDeviceByMountpoint(); +diff --git a/libpeony-qt/windows/format-dlg-create-delegate.cpp b/libpeony-qt/windows/format-dlg-create-delegate.cpp +index dad13eb..da7dd2d 100644 +--- a/libpeony-qt/windows/format-dlg-create-delegate.cpp ++++ b/libpeony-qt/windows/format-dlg-create-delegate.cpp +@@ -45,6 +45,7 @@ Format_Dialog *FormatDlgCreateDelegate::createUDiskDlg(const QString &uris, Side + { + if (m_udiskDlgMap.contains(uris)) { + m_udiskDlgMap[uris]->raise(); ++ m_udiskDlgMap[uris]->activateWindow(); + return m_udiskDlgMap[uris]; + } + +diff --git a/libpeony-qt/windows/format_dialog.cpp b/libpeony-qt/windows/format_dialog.cpp +index da72a21..6f5a145 100644 +--- a/libpeony-qt/windows/format_dialog.cpp ++++ b/libpeony-qt/windows/format_dialog.cpp +@@ -236,6 +236,13 @@ Format_Dialog::Format_Dialog(const QString &m_uris,SideBarAbstractItem *m_item,Q + connect(mFSCombox, &QComboBox::currentTextChanged, this, [=]{ + if (mFSCombox->currentText() == "ext4") { + QMessageBox::warning(nullptr, tr("Warning"), tr("Formatting to the ext4 file system may cause other users to be unable to read or write to the USB drive"), QMessageBox::Ok); ++ // Only perform window promotion in Wayland ++ bool isWayland = qApp->property("isWayland").toBool(); ++ if (isWayland) { ++ qDebug() << "Format_Dialog raise in wayland"; ++ this->raise(); ++ this->activateWindow(); ++ } + cryptCheckBox->setEnabled(true); + } else { + cryptCheckBox->setChecked(false); +@@ -323,17 +330,24 @@ Format_Dialog::Format_Dialog(const QString &m_uris,SideBarAbstractItem *m_item,Q + bool hasSetRomSize = false; + m_fs_type = FileUtils::getFileSystemType(m_uris); + //U disk or other mobile device, only use in iso system install device +- if (! m_unix_device.isEmpty() && m_fs_type.startsWith("iso") +- && ! m_uris.startsWith("computer:///WDC")) { +- char dev_name[256] ={0}; +- strncpy(dev_name, m_unix_device.toUtf8().constData(),sizeof(m_unix_device.toUtf8().constData()-1)); ++ if (!m_unix_device.isEmpty() && m_fs_type.startsWith("iso") ++ && !m_uris.startsWith("computer:///WDC")) { ++ QByteArray deviceData = m_unix_device.toUtf8(); ++ if (deviceData.size() >= 256) { ++ qWarning() << "Device name is too long!"; ++ return; // 提前退出,避免潜在问题 ++ } ++ ++ char dev_name[256] = {0}; // 初始化为0 ++ qstrncpy(dev_name, deviceData.constData(), sizeof(dev_name)); // 使用 qstrncpy ++ + auto size = FileUtils::getDeviceSize(dev_name); + if (size > 0) { + QString sizeInfo = QString::number(size, 'f', 1); +- qDebug() << "size:" <<size; ++ qDebug() << "size:" << size; + sizeInfo += "G"; +- mRomSizeCombox->clear (); +- mRomSizeCombox->addItem (sizeInfo); ++ mRomSizeCombox->clear(); ++ mRomSizeCombox->addItem(sizeInfo); + hasSetRomSize = true; + } + } +@@ -388,16 +402,23 @@ Format_Dialog::Format_Dialog(const QString &m_uris,SideBarAbstractItem *m_item,Q + + void Format_Dialog::colseFormat(bool) + { +- +- char dev_name[256] ={0}; +- //get device name ++ char dev_name[256] = {0}; ++ // get device name + QString volname, devName, voldisplayname; + +- //FIXME: replace BLOCKING api in ui thread. ++ // FIXME: replace BLOCKING api in UI thread. + FileUtils::queryVolumeInfo(fm_uris, volname, devName, voldisplayname); +- strncpy(dev_name,devName.toUtf8().constData(),sizeof(devName.toUtf8().constData()-1)); + +- //cancel format function ++ // 使用 QByteArray 检查长度并安全复制 ++ QByteArray devNameData = devName.toUtf8(); ++ if (devNameData.size() >= sizeof(dev_name)) { ++ qWarning() << "Device name is too long!"; ++ return; // 提前退出,避免潜在问题 ++ } ++ ++ qstrncpy(dev_name, devNameData.constData(), sizeof(dev_name)); // 使用 qstrncpy ++ ++ // cancel format function + cancel_format(dev_name); + } + +@@ -441,33 +462,60 @@ void Format_Dialog::slot_format(bool enable) + cryptCheckBox->setDisabled(true); + + //init the value +- char rom_size[1024] ={0},rom_type[1024]={0},rom_name[1024]={0},dev_name[1024]={0}; +- ++ char rom_size[1024] = {0}, rom_type[1024] = {0}, rom_name[1024] = {0}, dev_name[1024] = {0}; + + QString romType = mFSCombox->currentText(); + if (QString("vfat/fat32") == romType) { + romType = "vfat"; +- if (mNameEdit->text().trimmed ().toUtf8().length() <= 11){ +- strncpy(rom_name,mNameEdit->text().trimmed ().toUtf8().constData(), sizeof (rom_name) - 1); +- } ++ if (mNameEdit->text().trimmed().toUtf8().length() <= 11) { ++ QByteArray nameData = mNameEdit->text().trimmed().toUtf8(); ++ if (nameData.size() < sizeof(rom_name)) { ++ qstrncpy(rom_name, nameData.constData(), sizeof(rom_name)); ++ } else { ++ qWarning() << "Name is too long!"; ++ } ++ } + } else { +- strncpy(rom_name,mNameEdit->text().trimmed ().toUtf8().constData(), sizeof (rom_name) - 1); ++ QByteArray nameData = mNameEdit->text().trimmed().toUtf8(); ++ if (nameData.size() < sizeof(rom_name)) { ++ qstrncpy(rom_name, nameData.constData(), sizeof(rom_name)); ++ } else { ++ qWarning() << "Name is too long!"; ++ } + } + +- //get values from ui +- strncpy(rom_size,mRomSizeCombox->currentText ().toUtf8().constData(), strlen(mRomSizeCombox->currentText ().toUtf8().constData())); +- strncpy(rom_type, romType.toUtf8().constData(), strlen(romType.toUtf8().constData())); ++ // Get values from UI safely ++ QByteArray sizeData = mRomSizeCombox->currentText().toUtf8(); ++ if (sizeData.size() < sizeof(rom_size)) { ++ qstrncpy(rom_size, sizeData.constData(), sizeof(rom_size)); ++ } else { ++ qWarning() << "Size value is too long!"; ++ } + ++ QByteArray typeData = romType.toUtf8(); ++ if (typeData.size() < sizeof(rom_type)) { ++ qstrncpy(rom_type, typeData.constData(), sizeof(rom_type)); ++ } else { ++ qWarning() << "Type value is too long!"; ++ } + //disable name and rom size list + //ui->comboBox_rom_size->setDisabled(true); + this->mFSCombox->setDisabled(true); + +- QString volname, devName, voldisplayname ,devtype; +- //get device name +- //FIXME: replace BLOCKING api in ui thread. ++ QString volname, devName, voldisplayname, devtype; ++ ++ // Get device name ++ // FIXME: replace BLOCKING API in UI thread. + FileUtils::queryVolumeInfo(fm_uris, volname, devName, voldisplayname); + +- strncpy(dev_name,devName.toUtf8().constData(), sizeof (dev_name) - 1); ++ // 使用 QByteArray 和 qstrncpy 来安全复制字符串 ++ QByteArray devNameData = devName.toUtf8(); ++ if (devNameData.size() < sizeof(dev_name)) { ++ qstrncpy(dev_name, devNameData.constData(), sizeof(dev_name)); ++ } else { ++ qWarning() << "Device name is too long!"; ++ } ++ + devtype = rom_type; + + int format_value = 0; +@@ -730,17 +778,22 @@ void Format_Dialog::formatloop(){ + // } + + QString volname, devName, voldisplayname; +- static char name_dev[256] ={0}; +-// char prestr[10] = {0}; ++ static char name_dev[256] = {0}; + +- //cost time count ++ // Cost time count + m_cost_seconds++; + +- //FIXME: replace BLOCKING api in ui thread. ++ // FIXME: replace BLOCKING API in UI thread. + FileUtils::queryVolumeInfo(fm_uris, volname, devName, voldisplayname); + +- if(nullptr != devName) +- strcpy(name_dev,devName.toUtf8().constData()); ++ if (!devName.isEmpty()) { ++ QByteArray devNameData = devName.toUtf8(); ++ if (devNameData.size() < sizeof(name_dev)) { ++ qstrncpy(name_dev, devNameData.constData(), sizeof(name_dev)); ++ } else { ++ qWarning() << "Device name is too long!"; ++ } ++ } + + if (m_total_predict > 0) { + double cost = m_cost_seconds * 100.0/m_total_predict; +@@ -1351,5 +1404,19 @@ void Format_Dialog::resizeEvent(QResizeEvent *event) + mEraseCkbox->setText(mEraseCkbox->fontMetrics().elidedText(mEraseCkbox->text(), Qt::ElideRight, width)); + } + ++ updateButtonShow(mFormatBtn, mFormatBtn->text()); ++ updateButtonShow(mCancelBtn, mCancelBtn->text()); ++ + QWidget::resizeEvent(event); + } ++ ++void Format_Dialog::updateButtonShow(QPushButton *button, const QString &str) ++{ ++ int fontSize = button->fontMetrics().width(str); ++ QString tmp = str; ++ if (fontSize > button->width() - 5) { ++ button->setToolTip(str); ++ tmp = button->fontMetrics().elidedText(str, Qt::ElideRight, button->width() - 5); ++ } ++ button->setText(tmp); ++} +diff --git a/libpeony-qt/windows/format_dialog.h b/libpeony-qt/windows/format_dialog.h +index 824b91a..ed8f265 100644 +--- a/libpeony-qt/windows/format_dialog.h ++++ b/libpeony-qt/windows/format_dialog.h +@@ -116,6 +116,7 @@ public: + protected: + void closeEvent(QCloseEvent* ); + void resizeEvent(QResizeEvent *event); ++ void updateButtonShow(QPushButton *button, const QString &str); + + Q_SIGNALS: + void ensure_format(bool flags); +diff --git a/libpeony-qt/windows/ky-udf-format-dialog.cpp b/libpeony-qt/windows/ky-udf-format-dialog.cpp +index c64b83b..8fffac7 100644 +--- a/libpeony-qt/windows/ky-udf-format-dialog.cpp ++++ b/libpeony-qt/windows/ky-udf-format-dialog.cpp +@@ -80,6 +80,8 @@ UdfFormatDialog::UdfFormatDialog(const QString &uri, DiscControl *discControl, Q + + m_okBtn = new QPushButton; + m_okBtn->setText(tr("OK")); ++ m_okBtn->setDefault(true); ++ m_okBtn->setProperty("isImportant", true); + m_cancelBtn = new QPushButton; + m_cancelBtn->setText(tr("Cancel")); + m_mainLayout->addWidget(m_cancelBtn, 4, 5, 1, 2); +diff --git a/libpeony-qt/windows/properties-window-factory.cpp b/libpeony-qt/windows/properties-window-factory.cpp +index 5ab16ae..6a28618 100644 +--- a/libpeony-qt/windows/properties-window-factory.cpp ++++ b/libpeony-qt/windows/properties-window-factory.cpp +@@ -39,10 +39,10 @@ PropertiesWindowFactory *PropertiesWindowFactory::getInstance() + return globalInstance; + } + +-QMainWindow *PropertiesWindowFactory::create(const QStringList &uris) ++QMainWindow *PropertiesWindowFactory::create(const QStringList &uris, QWidget *parent) + { +- auto window = new PropertiesWindow(uris); +- return window; ++ m_window = new PropertiesWindow(uris, parent); ++ return m_window; + } + + void PropertiesWindowFactory::closeFactory() +@@ -61,3 +61,10 @@ bool PropertiesWindowFactory::unregisterFactory(QObject *factory) + PropertiesWindowTabPagePluginIface *Iface = dynamic_cast<PropertiesWindowTabPagePluginIface*>(factory); + return PropertiesWindowPluginManager::getInstance()->unregisterFactory(Iface); + } ++ ++void PropertiesWindowFactory::show() ++{ ++ if (m_window) { ++ m_window->show(); ++ } ++} +diff --git a/libpeony-qt/windows/properties-window-factory.h b/libpeony-qt/windows/properties-window-factory.h +index d75f145..7d307dd 100644 +--- a/libpeony-qt/windows/properties-window-factory.h ++++ b/libpeony-qt/windows/properties-window-factory.h +@@ -32,6 +32,7 @@ + namespace Peony { + + class PropertiesWindowTabPagePluginIface; ++class PropertiesWindow; + + /*! + * \brief The PropertiesWindowFactory class +@@ -70,7 +71,7 @@ public: + return true; + } + +- QMainWindow *create(const QStringList &uris); ++ QMainWindow *create(const QStringList &uris, QWidget *parent); + void closeFactory(); + + explicit PropertiesWindowFactory(QObject *parent = nullptr); +@@ -79,6 +80,10 @@ public: + bool registerFactory(QObject *factory); + bool unregisterFactory(QObject *factory); + ++ void show(); ++ ++private: ++ PropertiesWindow* m_window = nullptr; + }; + + } +diff --git a/libpeony-qt/windows/properties-window.cpp b/libpeony-qt/windows/properties-window.cpp +index f6de57c..7300f73 100644 +--- a/libpeony-qt/windows/properties-window.cpp ++++ b/libpeony-qt/windows/properties-window.cpp +@@ -34,6 +34,8 @@ + #include "thumbnail-manager.h" + #include "file-utils.h" + #include "vfs-plugin-manager.h" ++#include "volume-manager.h" ++#include "volumeManager.h" + #include "xatom-helper.h" + //#include "properties-window-factory.h" + +@@ -261,6 +263,8 @@ PropertiesWindow::PropertiesWindow(const QStringList &uris, QWidget *parent) : Q + } + }); + } ++ ++ this->onVolumeRemoveClosePropertiesPage(); + } + + void PropertiesWindow::init() +@@ -370,10 +374,42 @@ void PropertiesWindow::setWindowTitleTextAndIcon() + iconName = FileUtils::getFileIconName(m_fileInfo.get()->uri(), false); + } + ++ if(m_fileInfo->unixDeviceFile().startsWith("/dev/sr") ++ && (iconName.endsWith(".ico"))){/* 规范光盘图标,与竞品对比,光盘图标使用的是"media-optical",linkto bug#174770 */ ++ iconName = "media-optical"; ++ } ++ ++ if (!m_fileInfo->unixDeviceFile().isEmpty() && (m_fileInfo->unixDeviceFile().startsWith("/dev/sd") ++ || m_fileInfo->unixDeviceFile().startsWith("/dev/dm"))) { ++ std::shared_ptr<Volume> volume = nullptr; ++ QString targetUri = FileUtils::getTargetUri(m_fileInfo->uri()); ++ if (!targetUri.isEmpty()) { ++ volume = VolumeManager::getVolumeFromUri(targetUri.toUtf8().constData()); ++ if (volume) { ++ iconName = volume->iconName(); ++ } ++ } ++ ++ qDebug() << __LINE__ << __func__ << iconName << m_fileInfo->uri(); ++ ++ if (iconName == "drive-harddisk-usb") { ++ double size = FileUtils::getDeviceSize(m_fileInfo->unixDeviceFile().toUtf8().constData()); ++ if (size > 128) { ++ iconName = "drive-harddisk-usb"; ++ } else { ++ iconName = "drive-removable-media-usb"; ++ } ++ } ++ } ++ + if("computer:///ukui-data-volume" == m_fileInfo->uri()){ + windowTitle = tr("Data"); + iconName = "drive-harddisk"; + } ++ ++ if ("computer:///root.link" == m_fileInfo->uri()) { ++ windowTitle = tr("System Disk"); ++ } + } + } + } +@@ -394,10 +430,38 @@ void PropertiesWindow::setWindowTitleTextAndIcon() + }); + } + +- this->setWindowIcon(QIcon::fromTheme(iconName, QIcon::fromTheme("unknown"))); + this->setWindowTitle(windowTitle); +- headerBar->setIcon(iconName); + headerBar->setTitle(windowTitle); ++ /** ++ * @bug #267587: [Window Manager] Youhong Layout Reader has no icon in the upper left corner of the properties popup window. ++ * ++ * prioritize getting icons from the cache ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-09-26 ++ */ ++ auto icon = ThumbnailManager::getInstance()->tryGetThumbnail(m_fileInfo->uri()); ++ if (!icon.isNull()) { ++ qDebug() << __FILE__ << __FUNCTION__ << "tryGetThumbnail icon is not null"; ++ this->setWindowIcon(icon); ++ headerBar->setIcon(iconName); ++ return; ++ } ++ ++ icon = QIcon::fromTheme(iconName, QIcon::fromTheme("unknown")); ++ if (icon.name() == "unknown") { ++ QFileInfo iconInfo(iconName); ++ if (iconInfo.exists()) { ++ // Get the filename without suffix ++ QString iconNameWithoutSuffix = iconInfo.completeBaseName(); ++ qDebug() << __FILE__ << __FUNCTION__ << iconName << " iconNameWithoutSuffix: " << iconNameWithoutSuffix; ++ icon = QIcon::fromTheme(iconNameWithoutSuffix, QIcon::fromTheme("unknown")); ++ iconName = iconNameWithoutSuffix; ++ } ++ } ++ this->setWindowIcon(icon); ++ headerBar->setIcon(iconName); ++ return; + } + + void PropertiesWindow::notDir() +@@ -483,6 +547,8 @@ void PropertiesWindow::initStatusBar() + + okButton->setMinimumSize(PropertiesWindow::s_bottomButtonSize); + cancelButton->setMinimumSize(PropertiesWindow::s_bottomButtonSize); ++ okButton->setProperty("isImportant", true); ++ cancelButton->setProperty("useButtonPalette", true); + + //task#100231 trash page OK button set as restore + //fix bug#143817, trash properties issue +@@ -508,12 +574,12 @@ void PropertiesWindow::initTabPage(const QStringList &uris) + if(uris.isEmpty()) + return; + +- auto window = new PropertiesWindowPrivate(uris, this); +- window->tabBar()->setStyle(new tabStyle); ++ m_window = new PropertiesWindowPrivate(uris, this); ++ m_window->tabBar()->setStyle(new tabStyle); + //Warning: 不要设置tab高度,否则会导致tab页切换上下跳动 + //Do not set the tab height, otherwise it will cause the tab page to switch up and down + //window->tabBar()->setMinimumHeight(72); +- this->setCentralWidget(window); ++ this->setCentralWidget(m_window); + } + + bool PropertiesWindow::checkUriIsOpen(QStringList &uris, PropertiesWindow *newWindow) +@@ -531,6 +597,7 @@ bool PropertiesWindow::checkUriIsOpen(QStringList &uris, PropertiesWindow *newWi + qint64 index = PropertiesWindow::getOpenUriIndex(uris); + if (index != WINDOW_NOT_OPEN) { + openedPropertiesWindows->at(index)->raise(); ++ openedPropertiesWindows->at(index)->activateWindow(); + return true; + } + +@@ -699,6 +766,20 @@ void PropertiesWindow::paintEvent(QPaintEvent *event) + QWidget::paintEvent(event); + } + ++void PropertiesWindow::onVolumeRemoveClosePropertiesPage() ++{ ++ QString unixDevice; ++ if (m_uris.count() == 1 && m_uris.at(0).startsWith("computer:///")) { ++ unixDevice = FileUtils::getUnixDevice(m_uris.first()); ++ ++ connect(Experimental_Peony::VolumeManager::getInstance(), &Experimental_Peony::VolumeManager::volumeRemove, this, [=](const QString &removeDevice){ ++ qDebug() << __func__ << removeDevice; ++ if (unixDevice == removeDevice) { ++ saveAllChanged(); ++ } ++ }); ++ } ++} + + #ifdef KY_SDK_QT_WIDGETS + class TabBar : public kdk::KTabBar +@@ -721,6 +802,18 @@ protected: + }; + #endif + ++void PropertiesWindow::setOpenTabPage(const QString &className) ++{ ++ int index = -1; ++ for(auto &page : m_openTabPage) { ++ if (page->metaObject()->className() == className) { ++ index = m_openTabPage.indexOf(page); ++ break; ++ } ++ } ++ m_window->setCurrentIndex(index); ++} ++ + //properties window + PropertiesWindowPrivate::PropertiesWindowPrivate(const QStringList &uris, QWidget *parent) : QTabWidget(parent) + { +diff --git a/libpeony-qt/windows/properties-window.h b/libpeony-qt/windows/properties-window.h +index 085b372..1a0f222 100644 +--- a/libpeony-qt/windows/properties-window.h ++++ b/libpeony-qt/windows/properties-window.h +@@ -41,8 +41,9 @@ + namespace Peony { + + class PropertiesWindowTabPagePluginIface; ++class PropertiesWindowPrivate; + +-class PropertiesWindowPluginManager : public QObject ++class PEONYCORESHARED_EXPORT PropertiesWindowPluginManager : public QObject + { + friend class PropertiesWindow; + +@@ -215,6 +216,9 @@ public: + */ + bool handleKMREUri(QString &uri); + ++ void onVolumeRemoveClosePropertiesPage(); ++ void setOpenTabPage(const QString &className); ++ + protected: + /** + * 在窗口关闭时,将存储的窗口指针从openPropertiesWindows中删除 +@@ -231,6 +235,7 @@ private: + bool m_destroyThis = false; + QStringList m_uris; + QList<PropertiesWindowTabIface *> m_openTabPage; ++ PropertiesWindowPrivate* m_window = nullptr; + + + public: +diff --git a/plugin-iface/unstable/properties-window-factory-plugin-iface.h b/plugin-iface/unstable/properties-window-factory-plugin-iface.h +index 3c45e1c..15e5750 100644 +--- a/plugin-iface/unstable/properties-window-factory-plugin-iface.h ++++ b/plugin-iface/unstable/properties-window-factory-plugin-iface.h +@@ -43,10 +43,11 @@ public: + virtual ~PropertiesWindowFactoryPluginIface() {} + + virtual const QString version() = 0; +- virtual QMainWindow *create(const QStringList &uris) = 0; ++ virtual QMainWindow *create(const QStringList &uris, QWidget *parent) = 0; + virtual void closeFactory() = 0; + virtual bool registerFactory(QObject *factory) = 0; + virtual bool unregisterFactory(QObject *factory) = 0; ++ virtual void show() = 0; + }; + } + +diff --git a/src/control/file-label-box.cpp b/src/control/file-label-box.cpp +index 2a2c3e3..39cce42 100644 +--- a/src/control/file-label-box.cpp ++++ b/src/control/file-label-box.cpp +@@ -22,21 +22,17 @@ + + #include "file-label-box.h" + #include "file-label-model.h" +- + #include "label-box-delegate.h" ++#include "fm-window.h" + + #include <QMenu> +- + #include <QColorDialog> + #include <QMouseEvent> +- + #include <QPainter> + #include <QPainterPath> + #include <QPixmap> + #include <QMap> +- + #include <QStyleOptionViewItem> +- + #include <QApplication> + #include <QDebug> + +@@ -69,6 +65,25 @@ FileLabelBox::FileLabelBox(QWidget *parent) : QListView(parent) + // if (id > TOTAL_DEFAULT_COLOR) + // labelRemovable = true; + ++ Peony::FMWindowIface *windowIface = dynamic_cast<Peony::FMWindowIface *>(this->topLevelWidget()); ++ menu.addAction(QIcon::fromTheme("window-new-symbolic"), tr("Open In New Window"), [=](){ ++ int id = index.data(Qt::UserRole).toInt(); ++ if (id) ++ { ++ QString uri = "label:///" + QString::number(id); ++ auto newWindow = windowIface->create(uri); ++ dynamic_cast<QWidget *>(newWindow)->show(); ++ } ++ }); ++ menu.addAction(QIcon::fromTheme("tab-new-symbolic"), tr("Open In New Tab"), [=](){ ++ int id = index.data(Qt::UserRole).toInt(); ++ if (id) ++ { ++ QString uri = "label:///" + QString::number(id); ++ windowIface->addNewTabs(QStringList()<<uri); ++ } ++ }); ++ + menu.addAction(tr("Rename"), [=]() { + //FIXME: edit + edit(index); +@@ -205,9 +220,9 @@ void LabelBoxStyle::drawControl(QStyle::ControlElement element, const QStyleOpti + QSize LabelBoxStyle::sizeFromContents(QStyle::ContentsType type, const QStyleOption *option, const QSize &size, const QWidget *widget) const + { + if (type == CT_ItemViewItem) { +- QSize size = QApplication::style()->sizeFromContents(type, option, size, widget); +- size += QSize(0, 8); +- return size; ++ QSize tmpsize = QApplication::style()->sizeFromContents(type, option, size, widget); ++ tmpsize += QSize(0, 8); ++ return tmpsize; + } + return QApplication::style()->sizeFromContents(type, option, size, widget); + } +diff --git a/src/control/header-bar.cpp b/src/control/header-bar.cpp +index b012511..9ddaab0 100644 +--- a/src/control/header-bar.cpp ++++ b/src/control/header-bar.cpp +@@ -179,6 +179,7 @@ HeaderBar::HeaderBar(MainWindow *parent) : QToolBar(parent) + connect(goBack, &QPushButton::clicked, m_window, [=]() { + m_window->getCurrentPage()->goBack(); + Q_EMIT m_searchWidget->clearSearchBox(); ++ + }); + + connect(m_searchWidget, &Peony::SearchWidget::refreshRequest, [=]() { +@@ -188,14 +189,13 @@ HeaderBar::HeaderBar(MainWindow *parent) : QToolBar(parent) + m_window->getCurrentPage()->setSortFilter(index); + }); + +- connect(this, &HeaderBar::closeSearch, m_searchWidget, &Peony::SearchWidget::closeSearch ); + connect(this, &HeaderBar::setGlobalFlag, m_searchWidget, &Peony::SearchWidget::setGlobalFlag ); + connect(this, &HeaderBar::updateSearchRecursive, m_searchWidget, &Peony::SearchWidget::updateSearchRecursive); + connect(this, &HeaderBar::setLocation, m_searchWidget, &Peony::SearchWidget::updateLocation); + connect(this, &HeaderBar::cancelEdit, m_searchWidget, &Peony::SearchWidget::cancelEdit); + connect(this, &HeaderBar::startEdit, m_searchWidget, &Peony::SearchWidget::startEdit); + connect(this, &HeaderBar::finishEdit, m_searchWidget, &Peony::SearchWidget::finishEdit); +- connect(m_searchWidget, &Peony::SearchWidget::updateSearchRequest, this, &HeaderBar::updateSearchRequest); ++ //connect(m_searchWidget, &Peony::SearchWidget::updateSearchRequest, this, &HeaderBar::updateSearchRequest); + connect(m_searchWidget, &Peony::SearchWidget::updateLocationRequest, this, &HeaderBar::updateLocationRequest); + connect(this, &HeaderBar::setLocation, this, &HeaderBar::quitMultiSelect); + +@@ -300,6 +300,8 @@ HeaderBar::HeaderBar(MainWindow *parent) : QToolBar(parent) + connect(m_sort_type_menu, &QMenu::aboutToShow, m_sort_type_menu, [=]() { + bool originPathVisible = m_window->getCurrentUri() == "trash:///"; + m_sort_type_menu->setOriginPathVisible(originPathVisible); ++ bool isSearchTab = m_window->getCurrentUri().startsWith("search:///"); ++ m_sort_type_menu->setFilePathVisible(isSearchTab); + m_sort_type_menu->setSortType(m_window->getCurrentSortColumn()); + m_sort_type_menu->setSortOrder(m_window->getCurrentSortOrder()); + }); +@@ -535,10 +537,8 @@ void HeaderBar::switchSelectStatus(bool select) + m_actions.find(HeaderBarAction::Delete).value()->setVisible(false); + } + //fix bug#100105 After the selected status changes, the view type is grayed out. +- if (!select) { +- updateViewTypeEnable(); +- updateSortTypeEnable(); +- } ++ updateViewTypeEnable(); ++ updateSortTypeEnable(); + } + + void HeaderBar::addSpacing(int pixel) +@@ -578,7 +578,7 @@ void HeaderBar::updatePreviewPageVisible() + auto manager = Peony::PreviewPageFactoryManager::getInstance(); + auto pluginNames = manager->getPluginNames(); + for (auto name : pluginNames) { +- if (m_view_type_menu->menuAction()->isVisible() && m_preview_action->isChecked()) { ++ if (m_preview_action->isChecked() && m_preview_action->isVisible()) { + auto plugin = Peony::PreviewPageFactoryManager::getInstance()->getPlugin(name); + m_window->m_tab->setPreviewPage(plugin->createPreviewPage()); + } else { +diff --git a/src/control/navigation-side-bar.cpp b/src/control/navigation-side-bar.cpp +index 68f247b..317c1b1 100644 +--- a/src/control/navigation-side-bar.cpp ++++ b/src/control/navigation-side-bar.cpp +@@ -161,21 +161,50 @@ NavigationSideBar::NavigationSideBar(QWidget *parent) : QTreeView(parent) + m_proxy_model->invalidate();//display udisk in real time after format it. + }); + +- connect(this, &QTreeView::expanded, [=](const QModelIndex &index) { ++ auto globalSettings = Peony::GlobalSettings::getInstance(); ++ bool isShowNetwork = globalSettings->isExist(SHOW_NETWORK) ? globalSettings->getValue(SHOW_NETWORK).toBool() : true; ++ QStringList disExtensions = globalSettings->getValue(DISABLED_EXTENSIONS).toStringList(); ++ connect(this, &QTreeView::expanded, this, [=](const QModelIndex &index) { ++ // 获取索引项并缓存到局部变量 + auto item = m_proxy_model->itemFromIndex(index); +- qDebug()<<item->uri(); +- /*! +- \bug can not expanded? enumerator can not get prepared signal, why? +- */ +- bool isShowNetwork = Peony::GlobalSettings::getInstance()->isExist(SHOW_NETWORK) ? +- Peony::GlobalSettings::getInstance()->getValue(SHOW_NETWORK).toBool() : true; +- if (item->type() == SideBarAbstractItem::NetWorkItem && !isShowNetwork) { ++ auto itemType = item->type(); ++ auto itemUri = item->uri(); ++ ++ // 延迟加载子项 ++ item->findChildrenAsync(); ++ ++ if (itemType == SideBarAbstractItem::NetWorkItem && !isShowNetwork) { ++ // 如果不显示网络项目,则隐藏该行并退出 + this->setRowHidden(index.row(), index.parent(), true); + return; + } + +- item->findChildrenAsync(); +- }); ++ // 使用 QMap 缓存插件实例 ++ PluginManager* pluginManager = PluginManager::getInstance(); ++ QMap<QString, VFSPluginIface*> cachedPlugins; ++ ++ // 遍历禁用的扩展列表 ++ for (auto extension : disExtensions) { ++ // 如果插件未缓存,则动态转换并缓存 ++ if (!cachedPlugins.contains(extension)) { ++ VFSPluginIface* pIface = dynamic_cast<VFSPluginIface*>(pluginManager->getPluginByFileName(extension)); ++ cachedPlugins.insert(extension, pIface); ++ } else { ++ auto pIface = cachedPlugins[extension]; ++ ++ if (pIface && pIface->pluginType() == PluginInterface::VFSPlugin) { ++ bool isFileSystemItem = (itemType == SideBarAbstractItem::FileSystemItem); ++ bool isFavoriteItem = (itemType == SideBarAbstractItem::FavoriteItem); ++ ++ if ((isFileSystemItem && !itemUri.contains("computer:///") && itemUri.contains(pIface->uriScheme())) || ++ (isFavoriteItem && pIface->uriScheme() == "kmre://" && itemUri.contains(pIface->uriScheme()))) { ++ this->setRowHidden(index.row(), index.parent(), true); ++ return; ++ } ++ } ++ } ++ } ++ },Qt::QueuedConnection); + + connect(this, &QTreeView::collapsed, [=](const QModelIndex &index) { + auto item = m_proxy_model->itemFromIndex(index); +@@ -254,6 +283,11 @@ NavigationSideBar::NavigationSideBar(QWidget *parent) : QTreeView(parent) + auto curUri = item->uri(); + if (item->uri() == "computer:///ukui-data-volume") { + curUri = "file:///data"; ++ ++ //story 28545, improve data block solution, when has no user file in /data, go to usershare ++ //fix bug#239232, open in side bar menu not jump to usershare issue ++ if (Peony::FileUtils::isFileExsit("file:///data/usershare")) ++ curUri = "file:///data/usershare"; + } + + actionList << menu.addAction(QIcon::fromTheme("window-new-symbolic"), tr("Open In New Window"), [=](){ +@@ -447,6 +481,16 @@ NavigationSideBar::NavigationSideBar(QWidget *parent) : QTreeView(parent) + // continue; + // expand(index); + } ++ ++ /** ++ * @bug #278107: [Requirement 35207] [Start Menu] [Ribbon] Shortcut Entry-Computer Jump to the left side of the computer interface positioning error ++ * ++ * comment the following code ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-11-01 ++ */ ++#if 0 + /* 打开文件管理器默认聚焦在家目录上 */ + QString homeUri = "file://" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation); + QItemSelectionModel *selectionModel = this->selectionModel(); +@@ -461,6 +505,7 @@ NavigationSideBar::NavigationSideBar(QWidget *parent) : QTreeView(parent) + selectionModel->select(selection, QItemSelectionModel::Select); + } + }//end ++#endif + } + + void NavigationSideBar::sendKdkDataAsync() +@@ -538,6 +583,10 @@ bool NavigationSideBar::viewportEvent(QEvent *e) + QToolTip::hideText(); + e->ignore(); + return true; ++ } else { ++ QString itemText = firstColumnIndex.data(Qt::ToolTipRole).toString(); ++ QToolTip::showText(helpEvent->globalPos(), itemText); ++ return true; + } + } + return QTreeView::viewportEvent(e); +@@ -821,9 +870,10 @@ void NavigationSideBarContainer::addSideBar(NavigationSideBar *sidebar) + m_labelDialog->hide(); + m_layout->addWidget(m_labelDialog); + +- QWidget *w = new QWidget(this); +- QVBoxLayout *l = new QVBoxLayout; +- l->setContentsMargins(4, 4, 2, 4); ++ QWidget *buttonsContainer = new QWidget(this); ++ QVBoxLayout *buttonsLayout = new QVBoxLayout(buttonsContainer); ++ buttonsLayout->setContentsMargins(4, 4, 2, 4); ++ buttonsLayout->setSpacing(2); + + connect(m_labelDialog->selectionModel(), &QItemSelectionModel::selectionChanged, [=]() + { +@@ -832,8 +882,7 @@ void NavigationSideBarContainer::addSideBar(NavigationSideBar *sidebar) + int id = index.data(Qt::UserRole).toInt(); + if (id) + { +- //QString uri = "label:///" + QString::number(id); +- QString uri = "label:///" + name; ++ QString uri = "label:///" + QString::number(id); + Q_EMIT m_sidebar->updateWindowLocationRequest(uri); + } + }); +@@ -862,10 +911,8 @@ void NavigationSideBarContainer::addSideBar(NavigationSideBar *sidebar) + Peony::TagManagement::getInstance()->show(); + }); + +- l->setSpacing(0); +- +- l->addWidget(control); +- l->addWidget(labelButton); ++ buttonsLayout->addWidget(control); ++ buttonsLayout->addWidget(labelButton); + connect(labelButton, &QPushButton::clicked, this, [=](){ + bool checked = !labelButton->getShow(); + if (checked) { +@@ -882,8 +929,7 @@ void NavigationSideBarContainer::addSideBar(NavigationSideBar *sidebar) + m_labelDialog->setVisible(checked); + }); + +- w->setLayout(l); +- m_layout->addWidget(w); ++ m_layout->addWidget(buttonsContainer); + setLayout(m_layout); + + setTabOrder(m_sidebar, labelButton); +@@ -981,13 +1027,13 @@ TitleLabel::TitleLabel(QWidget *parent):QWidget(parent) + X11WindowManager::getInstance()->registerWidget(this); + m_pix_label = new QLabel(this); + //task#106007 【文件管理器】文件管理器应用做平板UI适配,修改应用图标可以跟随主题框架 +- m_pix_label->setPixmap(QIcon::fromTheme("system-file-manager").pixmap(32,32)); ++ m_pix_label->setPixmap(QIcon::fromTheme("system-file-manager").pixmap(24,24)); + + if (QGSettings::isSchemaInstalled("org.ukui.style")) { + m_gSettings = new QGSettings("org.ukui.style", QByteArray(), this); + connect(m_gSettings, &QGSettings::changed, this, [=](const QString &key) { + if("iconThemeName" == key){ +- m_pix_label->setPixmap(QIcon::fromTheme("system-file-manager").pixmap(32,32)); ++ m_pix_label->setPixmap(QIcon::fromTheme("system-file-manager").pixmap(24,24)); + } + }); + } +diff --git a/src/control/navigation-tab-bar.cpp b/src/control/navigation-tab-bar.cpp +index 57abb24..245498e 100644 +--- a/src/control/navigation-tab-bar.cpp ++++ b/src/control/navigation-tab-bar.cpp +@@ -49,6 +49,10 @@ + + #include <QStyleOption> + ++#include <QtX11Extras/QX11Info> ++ ++#include <kstartupinfo.h> ++ + #include "FMWindowIface.h" + #include "main-window.h" + #include "file-info.h" +@@ -61,8 +65,6 @@ NavigationTabBar::NavigationTabBar(QWidget *parent) : QTabBar(parent) + setProperty("isWindowButton", 0x1); + setProperty("useIconHighlightEffect", 0x2); + +- setFocusPolicy(Qt::StrongFocus); +- + setAcceptDrops(true); + m_drag_timer.setInterval(750); + m_drag_timer.setSingleShot(true); +@@ -126,9 +128,7 @@ void NavigationTabBar::updateLocation(int index, const QString &uri) + //qDebug() << "updateLocation text:" <<displayName <<uri << iconName; + if (uri.startsWith("search:///")) + { +- QString nameRegexp = Peony::SearchVFSUriParser::getSearchUriNameRegexp(uri); +- QString targetDirectory = Peony::SearchVFSUriParser::getSearchUriTargetDirectory(uri); +- displayName = tr("Search \"%1\" in \"%2\"").arg(nameRegexp).arg(targetDirectory); ++ displayName = Peony::SearchVFSUriParser::getSearchUriTargetDirectory(uri); + } + + //elide text if it is too long +@@ -315,6 +315,19 @@ void NavigationTabBar::mouseMoveEvent(QMouseEvent *e) + if (auto tab = qobject_cast<NavigationTabBar *>(d->target())) { + //do nothing for target tab bar helped us handling yet. + } else { ++#ifdef KSTARTUPINFO_HAS_SET_ICON_GEOMETRY ++ quint32 timeStamp = QX11Info::isPlatformX11() ? QX11Info::appUserTime() : 0; ++ KStartupInfoId startInfoId; ++ startInfoId.initId(KStartupInfo::createNewStartupIdForTimestamp(timeStamp)); ++ startInfoId.setupStartupEnv(); ++ KStartupInfoData startData; ++ startData.setHostname(); ++ startData.addPid(QCoreApplication::applicationPid()); ++ QRect rect(-1, -1, -1, -1); ++ startData.setIconGeometry(rect); ++ startData.setLaunchedBy(QCoreApplication::applicationPid()); ++ KStartupInfo::sendStartup(startInfoId, startData); ++#endif + auto window = dynamic_cast<Peony::FMWindowIface *>(this->topLevelWidget()); + auto newWindow = dynamic_cast<QWidget *>(window->create(this->tabData(currentIndex()).toString())); + newWindow->show(); +@@ -381,23 +394,26 @@ int TabBarStyle::pixelMetric(QStyle::PixelMetric metric, const QStyleOption *opt + + QRect TabBarStyle::subElementRect(QStyle::SubElement element, const QStyleOption *option, const QWidget *widget) const + { ++ int offset = 8; + if (!m_need_adjust) { +- return QProxyStyle::subElementRect(element, option, widget); ++ offset = 8; + } else { +- switch (element) { +- case SE_TabBarScrollLeftButton:{ +- QRect tabRect = option->rect; +- tabRect.setRight(tabRect.left() + 48); +- return tabRect; +- } +- case SE_TabBarScrollRightButton:{ +- QRect tabRect = option->rect; +- tabRect.setLeft(tabRect.right() - 48); +- return tabRect; +- } +- default: +- return QProxyStyle::subElementRect(element, option, widget); +- } ++ offset = 48; ++ } ++ ++ switch (element) { ++ case SE_TabBarScrollLeftButton:{ ++ QRect tabRect = option->rect; ++ tabRect.setRight(tabRect.left() + offset); ++ return tabRect; ++ } ++ case SE_TabBarScrollRightButton:{ ++ QRect tabRect = option->rect; ++ tabRect.setLeft(tabRect.right() - offset); ++ return tabRect; ++ } ++ default: ++ return QProxyStyle::subElementRect(element, option, widget); + } + } + +diff --git a/src/control/operation-menu.cpp b/src/control/operation-menu.cpp +index 5eab06a..954b2cb 100644 +--- a/src/control/operation-menu.cpp ++++ b/src/control/operation-menu.cpp +@@ -38,6 +38,9 @@ + #include <QMessageBox> + #include <QInputDialog> + #include <polkit/polkit.h> ++#include <QToolTip> ++#include <QSize> ++#include <QScreen> + + #include "global-settings.h" + #include "clipboard-utils.h" +@@ -49,6 +52,7 @@ + #include "file-meta-info.h" + #include "file-utils.h" + #include "extensions-manager-widget.h" ++#include "connect-to-server-dialog.h" + + OperationMenu::OperationMenu(MainWindow *window, QWidget *parent) : QMenu(parent) + { +@@ -237,6 +241,20 @@ setPasswd: + widget->show(); + }); + ++ m_showNetwork = addAction(tr("Show Network"), this, [=](bool checked){ ++ Peony::GlobalSettings::getInstance()->setValue(SHOW_NETWORK, checked); ++ }); ++ m_showNetwork->setCheckable(true); ++ ++ addAction(tr("Connect to Server"), this, [=](){ ++ Peony::ConnectServerDialog dlg; ++ if (dlg.exec()) { ++ if (!dlg.uri().isEmpty()) { ++ m_window->goToUri(dlg.uri(), true); ++ } ++ } ++ }); ++ + addSeparator(); + + //comment icon to design request +@@ -250,6 +268,12 @@ setPasswd: + dlg.setWindowModality(Qt::WindowModal); + dlg.exec(); + }); ++ ++#ifdef BUILD_WITH_SDK_FEEDBACK_MENU_ACTION ++ //task#335022, add feedback button in menu ++ kdk::KMenuButton* menubutton = new kdk::KMenuButton(); ++ addAction(menubutton->feedbackAction()); ++#endif + } + + void OperationMenu::updateMenu() +@@ -290,9 +314,12 @@ void OperationMenu::updateMenu() + Peony::GlobalSettings::getInstance()->getValue(SHOW_CREATE_TIME).toBool(): + false); + ++ m_showNetwork->setChecked(Peony::GlobalSettings::getInstance()->isExist(SHOW_NETWORK) ? ++ Peony::GlobalSettings::getInstance()->getValue(SHOW_NETWORK).toBool() : ++ true); ++ + //get window current directory and selections, then update ohter actions. + m_edit_widget->updateActions(m_window->getCurrentUri(), m_window->getCurrentSelections()); +- + bool tablet = qApp->property("tabletMode").toBool(); + m_editWidgetContainer->setVisible(!tablet); + if (tablet) { +@@ -409,6 +436,12 @@ OperationMenuEditWidget::OperationMenuEditWidget(MainWindow *window, QWidget *pa + trash->setProperty("useIconHighlightEffect", true); + trash->setProperty("iconHighlightEffectMode", 1); + trash->setProperty("fillIconSymbolicColor", true); ++ ++ // Install event filters for tooltip handling on operation buttons ++ installTooltipFilter(copy); ++ installTooltipFilter(paste); ++ installTooltipFilter(cut); ++ installTooltipFilter(trash); + } + + void OperationMenuEditWidget::updateActions(const QString ¤tDirUri, const QStringList &selections) +@@ -455,6 +488,10 @@ void OperationMenuEditWidget::updateActions(const QString ¤tDirUri, const + // isDirectoryCanWrite = false; + // } + ++ if(isSearch && selections.size()){/* hotfix bug#222786 */ ++ isDirectoryCanWrite = Peony::FileUtils::isSearchFilesParentWriteable(selections, isSearch); ++ } ++ + m_copy->setEnabled(!isSelectionEmpty && !isRecent && !isTrash && !isComputer); + m_cut->setEnabled(!isSelectionEmpty && !isDesktop && !isHome && !isRecent && !isTrash && !isComputer && isDirectoryCanWrite); + m_trash->setEnabled(!isSelectionEmpty && !isDesktop && !isHome && !isComputer && isDirectoryCanWrite && !hasLongFileName); +@@ -462,3 +499,72 @@ void OperationMenuEditWidget::updateActions(const QString ¤tDirUri, const + bool isClipboradHasFile = Peony::ClipboardUtils::isClipboardHasFiles(); + m_paste->setEnabled(isClipboradHasFile && !isSearch && !isRecent && !isTrash && !isComputer && !isFileBox && isDirectoryCanWrite); + } ++ ++/** ++ * @bug #288997: [File Manager] Mouse hover option bar for copy and paste operations, hover tips are displayed outside of the file manager. ++ * ++ * Handles tooltip events by: ++ * - Calculating appropriate tooltip position relative to mouse cursor ++ * - Ensuring tooltip remains within screen boundaries ++ * - Adjusting position if tooltip would overlap screen edges ++ * - Providing debug information about tooltip positioning ++ * ++ * Normally, just call setToolTip is ok. ++ * TODO: need to troubleshoot the underlying qt calculation of the position under wayland to solve this problem completely. ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-11-29 ++ */ ++bool OperationMenuEditWidget::eventFilter(QObject *watched, QEvent *event) ++{ ++ if (event->type() == QEvent::ToolTip) { ++ QToolButton *btn = qobject_cast<QToolButton*>(watched); ++ if (btn) { ++ QHelpEvent *he = static_cast<QHelpEvent*>(event); ++ QPoint globalPos = he->globalPos(); ++ ++ // Default offset values for tooltip positioning ++ const int VERTICAL_OFFSET = 5; ++ const int HORIZONTAL_OFFSET = 2; ++ // Position tooltip slightly below and to the right of cursor ++ globalPos += QPoint(HORIZONTAL_OFFSET, VERTICAL_OFFSET); ++ ++ // Calculate tooltip dimensions ++ QFontMetrics fm(QToolTip::font()); ++ QSize tooltipSize = fm.size(Qt::TextSingleLine, btn->toolTip()) + QSize(10, 6); ++ ++ // Adjust position to keep tooltip within screen boundaries ++ QScreen *screen = QGuiApplication::screenAt(globalPos); ++ if (screen) { ++ QRect screenGeometry = screen->geometry(); ++ ++ // Prevent tooltip from extending beyond right edge ++ if (globalPos.x() + tooltipSize.width() > screenGeometry.right()) { ++ // If it will go beyond the right border, offset it to the left ++ globalPos.setX(screenGeometry.right() - tooltipSize.width() - HORIZONTAL_OFFSET); ++ } ++ ++ // Prevent tooltip from extending beyond bottom edge ++ if (globalPos.y() + tooltipSize.height() > screenGeometry.bottom()) { ++ // If it will go beyond the lower border, display it above the mouse ++ globalPos.setY(he->globalPos().y() - tooltipSize.height() - VERTICAL_OFFSET); ++ } ++ } ++ ++ QToolTip::showText(globalPos, btn->toolTip()); ++ ++ // Output debug information ++ qDebug() << "btn Tooltip:" << btn->toolTip(); ++ qDebug() << "Mouse pos:" << he->globalPos(); ++ qDebug() << "Tooltip pos:" << globalPos; ++ qDebug() << "Tooltip size:" << tooltipSize; ++ ++ return true; ++ } ++ } ++ return QWidget::eventFilter(watched, event); ++} ++ ++void OperationMenuEditWidget::installTooltipFilter(QToolButton *btn) ++{ ++ btn->installEventFilter(this); ++} +diff --git a/src/control/operation-menu.h b/src/control/operation-menu.h +index 6e26ef8..41d83d4 100644 +--- a/src/control/operation-menu.h ++++ b/src/control/operation-menu.h +@@ -25,6 +25,8 @@ + + #include "about-dialog.h" + #include <QMenu> ++#include <QToolButton> ++#include <QEvent> + + class MainWindow; + class QToolButton; +@@ -48,6 +50,7 @@ private: + QAction *m_showCreateTime = nullptr; + QAction *m_showFoldersInNewWindow = nullptr; + QAction *m_showRelativeTime = nullptr; ++ QAction *m_showNetwork = nullptr; + + private: + MainWindow *m_window = nullptr; +@@ -68,6 +71,24 @@ private: + explicit OperationMenuEditWidget(MainWindow *window, QWidget *parent = nullptr); + + void updateActions(const QString ¤tDirUri, const QStringList &selections); ++ /** ++ * @brief Handles events for watched objects ++ * @param watched The object being watched ++ * @param event The event that occurred ++ * @return true if the event was handled, false otherwise ++ * ++ * This event filter specifically handles tooltip events for QToolButtons, ++ * calculating and adjusting tooltip positions to ensure they remain visible ++ * within screen boundaries. ++ */ ++ bool eventFilter(QObject *watched, QEvent *event); ++ /** ++ * @brief Installs tooltip event filter on a tool button ++ * @param btn The QToolButton to install the event filter on ++ * ++ * Sets up event filtering for tooltip display on the specified button. ++ */ ++ void installTooltipFilter(QToolButton *btn); + + QToolButton *m_copy = nullptr; + QToolButton *m_paste = nullptr; +diff --git a/src/control/search-widget.cpp b/src/control/search-widget.cpp +index ce5b69b..a65a4da 100644 +--- a/src/control/search-widget.cpp ++++ b/src/control/search-widget.cpp +@@ -38,17 +38,7 @@ SearchWidget::SearchWidget(QWidget *parent) : QWidget(parent) + { + QHBoxLayout *layout = new QHBoxLayout(this); + +- m_closeSearchButton = new QToolButton(this); +- m_closeSearchButton->setAutoRaise(false); +- m_closeSearchButton->setIcon(QIcon::fromTheme("mark-location-symbolic")); +- m_closeSearchButton->setToolTip(""); +- m_closeSearchButton->setVisible(false); +- + m_locationBar = new AdvancedLocationBar(this); +- m_searchButton = new QToolButton(this); +- m_searchButton->setIcon(QIcon::fromTheme("edit-find-symbolic")); +- m_searchButton->setToolTip(tr("Search")); +- m_searchButton->setAutoRaise(false); + + connect(m_locationBar, &AdvancedLocationBar::refreshRequest, this, &SearchWidget::refreshRequest); + +@@ -56,9 +46,14 @@ SearchWidget::SearchWidget(QWidget *parent) : QWidget(parent) + + connect(m_locationBar, &AdvancedLocationBar::searchRequest, [=](const QString &path, const QString &key){ + //key is null, clean search content, show all files ++ bool searchMode = m_locationBar->getSearchMode(); + if (key == "" || key.isNull()) { ++ Q_EMIT this->updateSearchRequest(searchMode); + Q_EMIT this->updateLocationRequest(path, false); + this->updateSearch(path, key, true); ++ if (!searchMode) { ++ m_searchGlobal = false; ++ } + } else { + if (m_searchGlobal) { + QString homePath = "file://" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation); +@@ -66,6 +61,7 @@ SearchWidget::SearchWidget(QWidget *parent) : QWidget(parent) + targetUri = targetUri.replace("&recursive=0", "&recursive=1"); + this->updateSearch(homePath, key, true); + } else { ++ Q_EMIT this->updateSearchRequest(searchMode); + auto targetUri = Peony::SearchVFSUriParser::parseSearchKey(path, key, true, false, "", m_searchRecursive); + targetUri = targetUri.replace("&recursive=1", "&recursive=0"); + this->updateSearch(path, key, true); +@@ -79,109 +75,26 @@ SearchWidget::SearchWidget(QWidget *parent) : QWidget(parent) + connect(this, &SearchWidget::finishEdit, m_locationBar, &AdvancedLocationBar::finishEdit); + connect(this, &SearchWidget::clearSearchBox, m_locationBar, &AdvancedLocationBar::clearSearchBox); + +- layout->addWidget(m_closeSearchButton); + layout->addWidget(m_locationBar); +- layout->addWidget(m_searchButton); +- layout->setSpacing(9); +- initAnimation(); +-} +- +-void SearchWidget::initAnimation() +-{ +- QPropertyAnimation *m_locationBarAnimation = new QPropertyAnimation(m_locationBar, "geometry"); +- m_locationBarAnimation->setDuration(300); +- m_locationBarAnimation->setEasingCurve(QEasingCurve::OutQuad); +- +- QPropertyAnimation *m_searchAnimation = new QPropertyAnimation(m_searchButton, "geometry"); +- m_searchAnimation->setDuration(300); +- m_searchAnimation->setEasingCurve(QEasingCurve::OutQuad); +- +- QPropertyAnimation *m_closeLocationBarAnimation = new QPropertyAnimation(m_locationBar, "geometry"); +- m_closeLocationBarAnimation->setDuration(300); +- m_closeLocationBarAnimation->setEasingCurve(QEasingCurve::OutQuad); +- +- QPropertyAnimation *m_closeSearchAnimation = new QPropertyAnimation(m_closeSearchButton, "geometry"); +- m_closeSearchAnimation->setDuration(300); +- m_closeSearchAnimation->setEasingCurve(QEasingCurve::OutQuad); +- +- QParallelAnimationGroup *m_closeSearchGroup = new QParallelAnimationGroup(this); +- m_closeSearchGroup->addAnimation(m_closeLocationBarAnimation); +- m_closeSearchGroup->addAnimation(m_searchAnimation); +- m_closeSearchGroup->setDirection(QAbstractAnimation::Forward); +- +- QParallelAnimationGroup *m_searchGroup = new QParallelAnimationGroup(this); +- m_searchGroup->addAnimation(m_locationBarAnimation); +- m_searchGroup->addAnimation(m_closeSearchAnimation); +- m_searchGroup->setDirection(QAbstractAnimation::Forward); +- +- connect(m_searchButton, &QToolButton::clicked, this, &SearchWidget::searchButtonClicked); +- connect(m_closeSearchButton, &QToolButton::clicked, this, &SearchWidget::searchButtonClicked); +- connect(this, &SearchWidget::changeSearchMode, this, [=](bool mode) { +- int actionSize = m_searchButton->sizeHint().width(); +- QRect locationBarRect = m_locationBar->geometry(); +- QRect searchRect = m_searchButton->geometry(); +- auto width = locationBarRect.width(); +- auto height = m_searchButton->sizeHint().height(); +- auto y = locationBarRect.y(); +- if (mode) { +- if (layoutDirection() == Qt::RightToLeft) { +- m_locationBarAnimation->setStartValue(QRect(9, y, actionSize, height)); +- m_locationBarAnimation->setEndValue(QRect(9, y, width,height)); +- m_closeSearchAnimation->setStartValue(QRect(this->width() - width - 9, searchRect.y(), width,actionSize)); +- m_closeSearchAnimation->setEndValue(QRect(this->width() - actionSize - 9, searchRect.y(), actionSize,actionSize)); +- } else { +- m_locationBarAnimation->setStartValue(QRect(this->width() - actionSize - 9, y, actionSize, height)); +- m_locationBarAnimation->setEndValue(QRect(this->width() - width - 9, y, width,height)); +- m_closeSearchAnimation->setStartValue(QRect(9, searchRect.y(), width,actionSize)); +- m_closeSearchAnimation->setEndValue(QRect(9, searchRect.y(), actionSize,actionSize)); +- } +- m_searchGroup->start(); +- } else { +- if (layoutDirection() == Qt::RightToLeft) { +- m_closeLocationBarAnimation->setStartValue(QRect(this->width() - actionSize - 9, y, actionSize,height)); +- m_closeLocationBarAnimation->setEndValue(QRect(actionSize + 9 + 9, y,width,height)); +- m_searchAnimation->setStartValue(QRect(9, searchRect.y(), width,actionSize)); +- m_searchAnimation->setEndValue(QRect(9, searchRect.y(), actionSize,actionSize)); +- } else { +- m_closeLocationBarAnimation->setStartValue(QRect(9, y, actionSize,height)); +- m_closeLocationBarAnimation->setEndValue(QRect(9, y,width,height)); +- m_searchAnimation->setStartValue(QRect(actionSize + 9, searchRect.y(), width,actionSize)); +- m_searchAnimation->setEndValue(QRect(this->width() - actionSize - 9, searchRect.y(), actionSize,actionSize)); +- } +- m_locationBar->setAnimationMode(true); +- m_closeSearchGroup->start(); +- } +- }); +- connect(m_closeSearchGroup, &QPropertyAnimation::finished, this, [=] { +- m_locationBar->setAnimationMode(false); +- }); ++ layout->setSpacing(102); + } + + void SearchWidget::searchButtonClicked() + { + m_searchMode = ! m_searchMode; +- m_searchButton->setVisible(! m_searchMode); + qDebug() << "searchButtonClicked" <<m_searchMode; + Q_EMIT this->updateSearchRequest(m_searchMode); + setSearchMode(m_searchMode); +- Q_EMIT changeSearchMode(m_searchMode); +- + } + + void SearchWidget::setSearchMode(bool mode) + { +- m_searchButton->setCheckable(mode); +- m_searchButton->setChecked(mode); +- m_searchButton->setDown(mode); +- m_closeSearchButton->setVisible(mode); + m_locationBar->switchEditMode(mode); + } + + void SearchWidget::closeSearch() + { + m_searchMode = false; +- m_searchButton->setVisible(true); +- m_closeSearchButton->setVisible(false); + setSearchMode(false); + } + +@@ -189,19 +102,16 @@ void SearchWidget::startEdit(bool bSearch) + { + //qDebug() << "bSearch" <<bSearch <<m_searchMode; + if (bSearch && m_searchMode) { ++ m_locationBar->setSearchBarFocus(); + return; + } + + if (bSearch) { + searchButtonClicked(); + } else { +- m_searchButton->setVisible(true); +- m_searchButton->setCheckable(false); +- m_closeSearchButton->setVisible(false); + m_locationBar->startEdit(); + m_locationBar->switchEditMode(false); + if (m_searchMode) { +- Q_EMIT changeSearchMode(false); + Q_EMIT updateSearchRequest(false); + } + m_searchMode = false; +@@ -219,16 +129,16 @@ void SearchWidget::updateSearchRecursive(bool recursive) + m_searchRecursive = recursive; + } + +-void SearchWidget::updateCloseSearch(QString icon) ++bool SearchWidget::isSearchMode() + { +- m_closeSearchButton->setIcon(QIcon::fromTheme(icon, QIcon::fromTheme("folder"))); +- m_closeSearchButton->setToolTip(icon); +- ++ return m_searchMode; + } + +-bool SearchWidget::isSearchMode() ++void SearchWidget::setSearchText(const QString &text) + { +- return m_searchMode; ++ if (m_locationBar) { ++ m_locationBar->setSearchText(text); ++ } + } + + void SearchWidget::updateTabletModeValue(bool isTabletMode) +diff --git a/src/control/search-widget.h b/src/control/search-widget.h +index d3278de..1664aee 100644 +--- a/src/control/search-widget.h ++++ b/src/control/search-widget.h +@@ -37,8 +37,8 @@ public: + explicit SearchWidget(QWidget *parent = nullptr); + void searchButtonClicked(); + void setSearchMode(bool mode); +- void updateCloseSearch(QString icon); + bool isSearchMode(); ++ void setSearchText(const QString &text); + + Q_SIGNALS: + void updateLocationRequest(const QString &uri, bool addHistory = true, bool force = true); +@@ -61,10 +61,6 @@ public Q_SLOTS: + void updateSearchProgress(bool isSearching); + + private: +- void initAnimation(); +- +- QToolButton *m_closeSearchButton; +- QToolButton *m_searchButton; + AdvancedLocationBar *m_locationBar; + + bool m_searchMode = false; +diff --git a/src/control/sort-type-menu.cpp b/src/control/sort-type-menu.cpp +index 34f17fb..d31ca56 100644 +--- a/src/control/sort-type-menu.cpp ++++ b/src/control/sort-type-menu.cpp +@@ -52,6 +52,10 @@ SortTypeMenu::SortTypeMenu(QWidget *parent) : QMenu(parent) + originalPath->setCheckable(true); + sortTypeGroup->addAction(originalPath); + ++ m_file_path = addAction(tr("Path")); ++ originalPath->setCheckable(true); ++ sortTypeGroup->addAction(m_file_path); ++ + connect(sortTypeGroup, &QActionGroup::triggered, this, [=](QAction *action) { + int index = sortTypeGroup->actions().indexOf(action); + switchSortTypeRequest(index); +@@ -95,6 +99,11 @@ void SortTypeMenu::setOriginPathVisible(bool visible) + m_origin_path->setVisible(visible); + } + ++void SortTypeMenu::setFilePathVisible(bool visible) ++{ ++ m_file_path->setVisible(visible); ++} ++ + QString SortTypeMenu::getSortTypeName(int type) + { + QString sortTypeName = ""; +diff --git a/src/control/sort-type-menu.h b/src/control/sort-type-menu.h +index ac656f0..2addefd 100644 +--- a/src/control/sort-type-menu.h ++++ b/src/control/sort-type-menu.h +@@ -32,6 +32,7 @@ public: + explicit SortTypeMenu(QWidget *parent = nullptr); + + void setOriginPathVisible(bool visible); ++ void setFilePathVisible(bool visible); + QString getSortTypeName(int type); + void updateSortOrderName(int type); + +@@ -49,6 +50,8 @@ private: + Qt::SortOrder m_sort_order = Qt::AscendingOrder; + + QAction *m_origin_path = nullptr; ++ QAction *m_file_path = nullptr; ++ + QActionGroup *m_sort_types; + QActionGroup *m_sort_orders; + }; +diff --git a/src/control/tab-widget.cpp b/src/control/tab-widget.cpp +index e1dd45a..172a39f 100644 +--- a/src/control/tab-widget.cpp ++++ b/src/control/tab-widget.cpp +@@ -172,6 +172,7 @@ TabWidget::TabWidget(QWidget *parent) : QMainWindow(parent) + + m_parent = parent; + m_tab_bar = new NavigationTabBar(this); ++ m_tab_bar->setFocusPolicy(Qt::TabFocus); + m_tab_bar->setLayoutDirection(layoutDirection()); + m_tab_bar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + m_stack = new QStackedWidget(this); +@@ -212,6 +213,7 @@ TabWidget::TabWidget(QWidget *parent) : QMainWindow(parent) + + //bug#94981 修改添加控件的位置和形状 + m_add_page_button = new QPushButton(this); ++ m_add_page_button->setFocusPolicy(Qt::TabFocus); + m_add_page_button->setFixedSize(QSize(38, 38)); + m_add_page_button->setIconSize(QSize(16, 16)); + m_add_page_button->setIcon(QIcon::fromTheme("list-add-symbolic")); +@@ -228,7 +230,7 @@ TabWidget::TabWidget(QWidget *parent) : QMainWindow(parent) + + m_show_page_button = new QToolButton(this); + m_show_page_button->setPopupMode(QToolButton::DelayedPopup); +- m_show_page_button->setFixedSize(QSize(48, 48)); ++ m_show_page_button->setFixedSize(QSize(38, 38)); + m_show_page_button->setIconSize(QSize(16, 16)); + m_show_page_button->setIcon(QIcon::fromTheme("ukui-all-tabs-symbolic")); + m_show_page_button->setAutoRaise(true); +@@ -240,7 +242,8 @@ TabWidget::TabWidget(QWidget *parent) : QMainWindow(parent) + m_treeView = new QTreeView(this); + m_treeView->setWindowFlag(Qt::Popup); + m_treeView->move(m_show_page_button->x(), m_tab_bar->sizeHint().height()); +- m_treeView->setFixedSize(QSize(162, 212)); ++// m_treeView->setFixedSize(QSize(162, 212)); ++ m_treeView->setFixedWidth(162); + m_treeView->setHeaderHidden(true); + m_treeView->header()->setStretchLastSection(true); + m_treeView->header()->setSectionResizeMode(QHeaderView::Fixed); +@@ -337,14 +340,14 @@ TabWidget::TabWidget(QWidget *parent) : QMainWindow(parent) + "QWidget#w {background-color: transparent;" + "border: 0px solid transparent;}"); + +- +- + auto vbox = new QVBoxLayout(); + m_top_layout = vbox; + vbox->setSpacing(0); + vbox->setContentsMargins(0, 0, 0, 0); + vbox->addLayout(trash); + vbox->addLayout(m_search_bar_layout); ++ //vbox->addLayout(m_search_filter_layout); ++ vbox->addWidget(m_filter_container); + + m_preview_splitter = new QSplitter(this); + m_preview_splitter->setChildrenCollapsible(false); +@@ -471,6 +474,25 @@ void TabWidget::setClassifyWidthWithFont(QComboBox *classifyCombox, int fontSize + } + } + ++void TabWidget::updateSearchFilterHeight() ++{ ++ m_filter_container->setMaximumHeight(m_search_filter_layout->getMaxinumHeight(m_filter_container->width())); ++ m_conditions_clear_btn->move(m_filter_container->geometry().right() - 46, m_filter_container->geometry().top() + 120); ++ m_conditions_clear_btn->raise(); ++} ++ ++void TabWidget::updateAdvanceShow(bool isVisible) ++{ ++ m_filter_container->setVisible(isVisible); ++ m_condition_label->setVisible(isVisible); ++ m_file_type_box->setVisible(isVisible); ++ m_file_mtime_box->setVisible(isVisible); ++ m_file_size_box->setVisible(isVisible); ++ m_file_label_box->setVisible(isVisible); ++ m_input_edit->setVisible(isVisible); ++ m_conditions_clear_btn->setVisible(isVisible); ++} ++ + void TabWidget::initAdvanceSearch() + { + //advance search bar +@@ -490,14 +512,132 @@ void TabWidget::initAdvanceSearch() + m_current_search->setFixedHeight(TRASH_BUTTON_HEIGHT + 20); + m_current_search->setStyleSheet("border: 1px solid transparent;"); + ++ QComboBox *searchTypeCommobox = new QComboBox(searchButtons); ++ m_search_type_box = searchTypeCommobox; ++ searchTypeCommobox->setFixedHeight(TRASH_BUTTON_HEIGHT); ++ searchTypeCommobox->setFixedWidth(TRASH_BUTTON_WIDTH * 3 + 15); ++ auto searchTypeModel = new QStringListModel(searchButtons); ++ searchTypeModel->setStringList(m_search_type_list); ++ searchTypeCommobox->setModel(searchTypeModel); ++ connect(searchTypeCommobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [=](int index){ ++ if (index > 0) { ++ //设置过滤关键字 ++ MainWindow *mainWindow = dynamic_cast<MainWindow *>(this->topLevelWidget()); ++ QString key = mainWindow->getLastSearchKey(); ++ qDebug() << __func__ << "key: " << key; ++ this->updateFilterContent(key); ++ } else { ++ bool searchIndex = isSearchIndex(); ++ if (!searchIndex) { ++ int ret = QMessageBox::question(nullptr, tr("Search Settings"), tr("After the creation of the index, " ++ "the next search can get the results of the document content containing the search term, " ++ "during which you can exit the page at any time, " ++ "we will continue to complete the creation in the background.")); ++ if (QMessageBox::Yes == ret) { ++ const QByteArray id(UKUI_SEARCH_SCHEMAS); ++ if (QGSettings::isSchemaInstalled(id)) { ++ QGSettings *searchSettings = new QGSettings(id, QByteArray(), this); ++ if (searchSettings && searchSettings->keys().contains(SEARCH_METHOD_KEY)) { ++ searchSettings->set(SEARCH_METHOD_KEY, true); ++ searchSettings->set("contentIndexEnable", true); ++ searchSettings->set("contentIndexEnableOcr", true); ++ } ++ } ++ } ++ m_search_type_box->setCurrentIndex(1); ++ } ++ ++ //清除过滤关键字 ++ currentPage()->clearFileContentConditions(); ++ updateFilter(); ++ } ++ }); ++ + m_home_search = new QPushButton(tr("Computer"), this); + // m_home_search->setFixedWidth(TRASH_BUTTON_WIDTH + 50); + m_home_search->setFixedHeight(TRASH_BUTTON_HEIGHT + 20); + m_home_search->setStyleSheet("border: 1px solid transparent;"); + +- m_add_filter_button = new QPushButton(QIcon::fromTheme("list-add-symbolic"), "", this); +- m_add_filter_button->setFixedHeight(TRASH_BUTTON_HEIGHT + 20); +- m_add_filter_button->setStyleSheet("border: 1px solid transparent;"); ++ m_add_filter_button = new QPushButton(QIcon::fromTheme("list-add-symbolic"), "Filter", this); ++ m_add_filter_button->setCheckable(true); ++ m_add_filter_button->setFixedHeight(TRASH_BUTTON_HEIGHT); ++ connect(m_add_filter_button, &QPushButton::toggled, this, [=](bool checked){ ++ if (checked) { ++ updateAdvanceShow(true); ++ updateSearchFilterHeight(); ++ } else { ++ updateAdvanceShow(false); ++ } ++ }); ++ ++ m_filter_container = new QWidget(this); ++ m_filter_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); ++ m_filter_container->setContentsMargins(0, 0, 0, 0); ++ ++ m_file_label_model = FileLabelModel::getGlobalModel(); ++ QList<FileLabelItem *> allLabels = m_file_label_model->getAllFileLabelItems(); ++ ++ m_search_filter_layout = new FlowLayout(10, 12, 15); ++ m_condition_label = new QLabel(tr("Condition")); ++ m_file_type_box = new Peony::MultiSelectComboBox(); ++ m_file_type_box->addItems(m_file_type_list); ++ m_file_type_box->setPlaceholderText(tr("File Type")); ++ ++ m_file_mtime_box = new Peony::MultiSelectComboBox(); ++ m_file_mtime_box->addItems(m_file_mtime_list); ++ m_file_mtime_box->setPlaceholderText(tr("Modify time")); ++ ++ m_file_size_box = new Peony::MultiSelectComboBox(); ++ m_file_size_box->addItems(m_file_size_list); ++ m_file_size_box->setPlaceholderText(tr("File Size")); ++ ++ m_file_label_box = new Peony::MultiSelectComboBox(); ++ m_file_label_box->setPlaceholderText(tr("File Label")); ++ ++ for (int i = 0; i < allLabels.size(); ++i) { ++ m_file_label_box->addItem(allLabels.at(i)->name(), false, 0, allLabels.at(i)->color()); ++ } ++ ++ m_input_edit = new QLineEdit(); ++ m_input_edit->setFixedHeight(TRASH_BUTTON_HEIGHT); ++ m_input_edit->setFixedWidth(TRASH_BUTTON_WIDTH *4); ++ m_input_edit->setPlaceholderText(tr("Please input key words...")); ++ m_input_edit->setText(""); ++ //m_input_edit->setTextMargins(0, 0, 20, 0); ++ ++ m_conditions_clear_btn = new QPushButton(this); ++ m_conditions_clear_btn->setIcon(QIcon::fromTheme("edit-delete-symbolic")); ++ m_conditions_clear_btn->setFixedSize(TRASH_BUTTON_HEIGHT, TRASH_BUTTON_HEIGHT); ++ ++ m_search_filter_layout->addWidget(m_condition_label); ++ m_search_filter_layout->addWidget(m_file_type_box); ++ m_search_filter_layout->addWidget(m_file_mtime_box); ++ m_search_filter_layout->addWidget(m_file_size_box); ++ m_search_filter_layout->addWidget(m_file_label_box); ++ m_search_filter_layout->addWidget(m_input_edit); ++ ++ m_filter_container->setLayout(m_search_filter_layout); ++ connect(m_file_type_box, &Peony::MultiSelectComboBox::hidingPopup, this, &TabWidget::updateAdvanceConditions); ++ connect(m_file_mtime_box, &Peony::MultiSelectComboBox::hidingPopup, this, &TabWidget::updateAdvanceConditions); ++ connect(m_file_size_box, &Peony::MultiSelectComboBox::hidingPopup, this, &TabWidget::updateAdvanceConditions); ++ connect(m_file_label_box, &Peony::MultiSelectComboBox::hidingPopup, this, &TabWidget::updateAdvanceConditions); ++ connect(m_file_type_box, &Peony::MultiSelectComboBox::showingPopup, this, &TabWidget::updateMultiComboBoxCount); ++ connect(m_file_mtime_box, &Peony::MultiSelectComboBox::showingPopup, this, &TabWidget::updateMultiComboBoxCount); ++ connect(m_file_size_box, &Peony::MultiSelectComboBox::showingPopup, this, &TabWidget::updateMultiComboBoxCount); ++ connect(m_file_label_box, &Peony::MultiSelectComboBox::showingPopup, this, &TabWidget::updateMultiComboBoxCount); ++ connect(m_input_edit, &QLineEdit::textChanged, this, &TabWidget::updateAdvanceConditions); ++ connect(m_conditions_clear_btn, &QPushButton::clicked, this, [=](){ ++ for (int i = 0; i < m_conditions_list.size(); ++i) { ++ m_conditions_list.at(i)->uncheckAllItem(); ++ } ++ m_file_label_box->uncheckAllItem(); ++ m_input_edit->setText(""); ++ updateAdvanceConditions(); ++ }); ++ ++ m_conditions_list.append(m_file_type_box); ++ m_conditions_list.append(m_file_size_box); ++ m_conditions_list.append(m_file_mtime_box); + + connect(m_home_search, &QPushButton::clicked, m_home_search, [=]() { + m_jumpToComputer = true; +@@ -512,13 +652,14 @@ void TabWidget::initAdvanceSearch() + } + }); + +- connect(m_add_filter_button, &QPushButton::clicked, this, &TabWidget::addNewConditionBar); +- + search->addWidget(title, 0, Qt::AlignLeft); + search->addSpacing(10); + search->addWidget(m_current_search, 0, Qt::AlignLeft); ++ + search->addSpacing(10); + search->addWidget(m_home_search, 0, Qt::AlignLeft); ++ search->addSpacing(10); ++ search->addWidget(searchTypeCommobox, Qt::AlignLeft); + search->addStretch(1); + search->addWidget(searchButtons); + search->addWidget(m_add_filter_button, 0, Qt::AlignRight); +@@ -528,6 +669,8 @@ void TabWidget::initAdvanceSearch() + m_current_search->setVisible(false); + m_home_search->setVisible(false); + m_add_filter_button->setVisible(false); ++ searchTypeCommobox->setVisible(false); ++ updateAdvanceShow(false); + } + + //search conditions changed, update filter +@@ -601,267 +744,6 @@ void TabWidget::browsePath() + } + } + +-void TabWidget::addNewConditionBar() +-{ +- QHBoxLayout *layout = new QHBoxLayout(); +- m_layout_list.append(layout); +- +- QToolBar *optionBar = new QToolBar(this); +- m_search_bar_list.append(optionBar); +- +- QComboBox *conditionCombox = new QComboBox(optionBar); +- m_conditions_list.append(conditionCombox); +- conditionCombox->setFixedHeight(TRASH_BUTTON_HEIGHT); +- if (QGSettings::isSchemaInstalled("org.ukui.style")) { +- QGSettings *fontSetting = new QGSettings(FONT_SETTINGS, QByteArray(), this); +- double fontSize = fontSetting->get("systemFontSize").toDouble(); +- setCondWidthWithFont(conditionCombox, fontSize); +- }else{ +- conditionCombox->setFixedWidth(TRASH_BUTTON_WIDTH *2); +- } +- +- auto conditionModel = new QStringListModel(optionBar); +- conditionModel->setStringList(m_option_list); +- conditionCombox->setModel(conditionModel); +- auto index = m_search_bar_count; +- if (index > m_option_list.count()-1) +- index = m_option_list.count()-1; +- conditionCombox->setCurrentIndex(index); +- +- //qDebug() << "addNewConditionBar:" <<index; +- +- QLabel *linkLabel = new QLabel(tr("is")); +- m_link_label_list.append(linkLabel); +- linkLabel->setFixedHeight(TRASH_BUTTON_HEIGHT); +- linkLabel->setFixedWidth(TRASH_BUTTON_HEIGHT); +- +- QComboBox *classifyCombox = new QComboBox(optionBar); +- m_classify_list.append(classifyCombox); +- classifyCombox->setFixedHeight(TRASH_BUTTON_HEIGHT); +- if (QGSettings::isSchemaInstalled("org.ukui.style")) { +- QGSettings *fontSetting = new QGSettings(FONT_SETTINGS, QByteArray(), this); +- double fontSize = fontSetting->get("systemFontSize").toDouble(); +- setClassifyWidthWithFont(classifyCombox, fontSize); +- } +- else{ +- classifyCombox->setFixedWidth(TRASH_BUTTON_WIDTH *2+45); +- } +- +- auto classifyModel = new QStringListModel(optionBar); +- auto list = getCurrentClassify(index); +- classifyModel->setStringList(list); +- classifyCombox->setModel(classifyModel); +- +- QLineEdit *inputBox = new QLineEdit(optionBar); +- m_input_list.append(inputBox); +- inputBox->setFixedHeight(TRASH_BUTTON_HEIGHT); +- inputBox->setFixedWidth(TRASH_BUTTON_WIDTH *4); +- inputBox->setPlaceholderText(tr("Please input key words...")); +- inputBox->setText(""); +- //fix bug#180920, contents and icon overlap issue +- inputBox->setTextMargins(0, 0, 20, 0); +- +- //bug#93521 添加清除按钮 +- QToolButton* clearButton = new QToolButton(inputBox); +- clearButton->setAttribute(Qt::WA_TranslucentBackground); +- clearButton->setObjectName("toolButton"); +- clearButton->installEventFilter(this); +- clearButton->setAutoRaise(true); +- clearButton->setStyle(TabBarStyle::getStyle()); +- clearButton->setFixedSize(inputBox->height() - 4, inputBox->height() - 4); +- QHBoxLayout* clearlayout = new QHBoxLayout(inputBox); +- clearlayout->addStretch(); +- clearlayout->addWidget(clearButton,Qt::AlignRight); +- clearlayout->setMargin(2); +- inputBox->setLayout(clearlayout); +- clearButton->setIcon(QIcon::fromTheme("edit-clear-symbolic")); +- clearButton->setProperty("isWindowButton", 1); +- //clearButton->setProperty("useIconHighlightEffect", 0x2); +- //clearButton->setAutoRaise(true); +- clearButton->hide(); +- +- +- connect(clearButton, &QPushButton::clicked, this, [=](){ +- inputBox->clear(); +- }); +- connect(inputBox, &QLineEdit::textChanged, this, [=](const QString &text){ +- if(text.isEmpty()) +- { +- clearButton->hide(); +- } +- else +- { +- clearButton->show(); +- } +- }); +- +- QPushButton *addButton = new QPushButton(QIcon::fromTheme("list-add-symbolic"), "", optionBar); +- m_add_button_list.append(addButton); +- addButton->setFixedHeight(20); +- addButton->setFixedWidth(20); +- addButton->setFlat(true); +- addButton->setProperty("isWindowButton", 1); +- //addButton->setProperty("useIconHighlightEffect", 2); +- addButton->setProperty("isIcon", true); +- connect(addButton, &QPushButton::clicked, this, &TabWidget::addNewConditionBar); +- +- QPushButton *removeButton = new QPushButton(QIcon::fromTheme("list-remove-symbolic"), "", optionBar); +- m_remove_button_list.append(removeButton); +- removeButton->setFixedHeight(20); +- removeButton->setFixedWidth(20); +- removeButton->setFlat(true); +- removeButton->setProperty("isWindowButton", 1); +- //removeButton->setProperty("useIconHighlightEffect", 2); +- removeButton->setProperty("isIcon", true); +- //mapper for button clicked parse index +- auto signalMapper = new QSignalMapper(this); +- connect(removeButton, SIGNAL(clicked()), signalMapper, SLOT(map())); +- signalMapper->setMapping(removeButton, index); +- connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(removeConditionBar(int))); +- m_remove_mapper_list.append(signalMapper); +- +-// layout->addWidget(addButton, Qt::AlignRight); +-// layout->addSpacing(10); +-// layout->addWidget(removeButton, Qt::AlignRight); +-// layout->addSpacing(10); +-// layout->addSpacing(TRASH_BUTTON_WIDTH - 20); +- //对齐搜索按钮,留出间距 +- layout->addSpacing(m_search_title->width() + 10); +- layout->addWidget(conditionCombox, Qt::AlignLeft); +- layout->addSpacing(10); +- layout->addWidget(linkLabel, Qt::AlignLeft); +- layout->addSpacing(10); +- layout->addWidget(classifyCombox, Qt::AlignLeft); +- layout->addWidget(inputBox, Qt::AlignLeft); +- layout->addWidget(optionBar); +- layout->addWidget(removeButton, Qt::AlignRight); +- layout->addSpacing(10); +- layout->addWidget(addButton, Qt::AlignRight); +- layout->setContentsMargins(10, 0, 10, 5); +- +- if (index%4 >= 3) +- { +- classifyCombox->hide(); +- linkLabel->setText(tr("contains")); +- //adjust label width to language +- //use 1.5 rate width to fix big size font issue, link to bug#58824 +- QLocale locale; +- if (locale.language() == QLocale::Chinese) +- linkLabel->setFixedWidth(1.5 * TRASH_BUTTON_HEIGHT); +- else +- linkLabel->setFixedWidth(TRASH_BUTTON_WIDTH); +- } +- else +- { +- inputBox->hide(); +- } +- +- +- connect(conditionCombox, &QComboBox::currentTextChanged, [=]() +- { +- auto cur = conditionCombox->currentIndex(); +- if (cur%4 >= 3) +- { +- classifyCombox->setCurrentIndex(0); +- classifyCombox->hide(); +- inputBox->show(); +- linkLabel->setText(tr("contains")); +- //adjust label width to language +- //use 1.5 rate width to fix big size font issue, link to bug#58824 +- QLocale locale; +- if (locale.language() == QLocale::Chinese) +- linkLabel->setFixedWidth(1.5 * TRASH_BUTTON_HEIGHT); +- else +- linkLabel->setFixedWidth(TRASH_BUTTON_WIDTH); +- } +- else +- { +- classifyCombox->show(); +- //clear old filter conditions, fix bug#83559 +- inputBox->setText(""); +- inputBox->hide(); +- linkLabel->setFixedWidth(TRASH_BUTTON_HEIGHT); +- linkLabel->setText(tr("is")); +- auto classifyList = getCurrentClassify(cur); +- classifyModel->setStringList(classifyList); +- classifyCombox->setModel(classifyModel); +- classifyCombox->setCurrentIndex(0); +- } +- }); +- +- connect(classifyCombox, &QComboBox::currentTextChanged, this, &TabWidget::updateAdvanceConditions); +- connect(inputBox, &QLineEdit::textChanged, this, &TabWidget::updateAdvanceConditions); +- +- m_top_layout->insertLayout(m_top_layout->count()-1, layout); +- m_search_bar_count++; +- updateAdvanceConditions(); +- updateButtons(); +-} +- +-void TabWidget::removeConditionBar(int index) +-{ +- //disconnect signals after index search bars +- for(int cur=0; cur<m_layout_list.count(); cur++) +- { +- disconnect(m_add_button_list[cur], &QPushButton::clicked, this, &TabWidget::addNewConditionBar); +- disconnect(m_remove_button_list[cur], SIGNAL(clicked()), m_remove_mapper_list[cur], SLOT(map())); +- disconnect(m_remove_mapper_list[cur], SIGNAL(mapped(int)), this, SLOT(removeConditionBar(int))); +- } +- +- //qDebug() << "removeConditionBar:" <<index <<m_conditions_list.count(); +- m_layout_list[index]->deleteLater(); +- m_conditions_list[index]->deleteLater(); +- m_link_label_list[index]->deleteLater(); +- m_classify_list[index]->deleteLater(); +- m_input_list[index]->deleteLater(); +- m_search_bar_list[index]->deleteLater(); +- m_add_button_list[index]->deleteLater(); +- m_remove_button_list[index]->deleteLater(); +- m_remove_mapper_list[index]->deleteLater(); +- +- m_layout_list.removeAt(index); +- m_conditions_list.removeAt(index); +- //qDebug() << "removeConditionBar:"<<m_conditions_list.count(); +- m_link_label_list.removeAt(index); +- m_classify_list.removeAt(index); +- m_input_list.removeAt(index); +- m_search_bar_list.removeAt(index); +- m_add_button_list.removeAt(index); +- m_remove_button_list.removeAt(index); +- m_remove_mapper_list.removeAt(index); +- +- //reconnect signals after index search bars +- for(int cur=0; cur<m_layout_list.count(); cur++) +- { +- connect(m_add_button_list[cur], &QPushButton::clicked, this, &TabWidget::addNewConditionBar); +- connect(m_remove_button_list[cur], SIGNAL(clicked()), m_remove_mapper_list[cur], SLOT(map())); +- m_remove_mapper_list[cur]->setMapping(m_remove_button_list[cur], cur); +- connect(m_remove_mapper_list[cur], SIGNAL(mapped(int)), this, SLOT(removeConditionBar(int))); +- } +- m_search_bar_count--; +- updateAdvanceConditions(); +- updateButtons(); +-} +- +-QStringList TabWidget::getCurrentClassify(int rowCount) +-{ +- QStringList currentList; +- currentList.clear(); +- +- switch (rowCount%4) { +- case 0: +- return m_file_type_list; +- case 1: +- return m_file_size_list; +- case 2: +- return m_file_mtime_list; +- default: +- break; +- } +- +- return currentList; +-} +- + void TabWidget::updateStatusBarSliderState() + { + if (currentPage() && currentPage()->getView()) { +@@ -889,11 +771,6 @@ void TabWidget::updateTrashBarVisible(const QString &uri) + m_trash_label->setVisible(visible); + m_clear_button->setVisible(visible); + m_recover_button->setVisible(visible); +- +-// if (uri.startsWith("trash://") || uri.startsWith("recent://")) +-// m_tool_bar->setVisible(false); +-// else +-// m_tool_bar->setVisible(true); + } + + void TabWidget::handleZoomLevel(int zoomLevel) +@@ -929,34 +806,6 @@ void TabWidget::handleZoomLevel(int zoomLevel) + } + } + #include"windows/FMWindowIface.h" +-void TabWidget::enableSearchBar(bool enable) +-{ +- //qDebug() << "enable:" <<enable; +- //m_search_path->setEnabled(enable); +- //m_search_close->setEnabled(enable); +- m_search_title->setEnabled(enable); +- m_search_bar->setEnabled(enable); +- if (m_search_bar_count >0) +- { +- //already had a list,just set to show +- for(int i=0; i<m_search_bar_list.count(); i++) +- { +- m_conditions_list[i]->setEnabled(enable); +- m_link_label_list[i]->setEnabled(enable); +- if (m_conditions_list[i]->currentIndex()%4 < 3) +- m_classify_list[i]->setEnabled(enable); +- else +- m_input_list[i]->setEnabled(enable); +- m_search_bar_list[i]->setEnabled(enable); +- m_add_button_list[i]->setEnabled(enable); +- /* When there is only one filter item,remove button set disable */ +- if(m_search_bar_count==1) +- m_remove_button_list[0]->setEnabled(false); +- else +- m_remove_button_list[i]->setEnabled(enable); +- } +- } +-} + + #include <KWindowSystem> + void TabWidget::slot_responseUnmounted(const QString &destUri, const QString &sourceUri) +@@ -991,10 +840,88 @@ void TabWidget::slot_responseUnmounted(const QString &destUri, const QString &so + } + } + ++void TabWidget::updateSearchTypeShow() ++{ ++ //判断是否建立索引更新默认选项 ++ bool index = isSearchIndex(); ++ if (index) { ++ m_search_type_box->setCurrentIndex(0); ++ } else { ++ m_search_type_box->setCurrentIndex(1); ++ } ++} ++ ++void TabWidget::updateFilterContent(const QString &key) ++{ ++ if(!currentPage()) ++ return; ++ ++ currentPage()->clearFileContentConditions(); ++ if (m_search_type_box->currentIndex()) { ++ currentPage()->addFileContentFilter(key); ++ } ++ ++ updateFilter(); ++} ++ ++bool TabWidget::isSearchIndex() ++{ ++ bool isSearchIndex = false; ++ const QByteArray id(UKUI_SEARCH_SCHEMAS); ++ if (QGSettings::isSchemaInstalled(id)) { ++ QGSettings *searchSettings = new QGSettings(id, QByteArray(), this); ++ if (searchSettings && searchSettings->keys().contains(SEARCH_METHOD_KEY)) { ++ isSearchIndex = searchSettings->get(SEARCH_METHOD_KEY).toBool(); ++ } ++ } ++ ++ return isSearchIndex; ++} ++ ++void TabWidget::setMutipleLabelConditions(QStringList names, QList<QColor> colors) ++{ ++ if(!currentPage()) ++ return; ++ currentPage()->setMutipleLabelConditions(names, colors); ++} ++ ++void TabWidget::updateMultiComboBoxCount() ++{ ++ if (!currentPage()) ++ return; ++ QMap<int, int> fileTypeMap = currentPage()->getFileTypeCount(); ++ for (int i = 0; i < m_file_type_box->count(); ++i) { ++ m_file_type_box->setCountFromItemIndex(i, fileTypeMap.value(i + 1)); ++ } ++ ++ QMap<int, int> fileModifyTimeMap = currentPage()->getFileModifyTimeCount(); ++ for (int i = 0; i < m_file_mtime_box->count(); ++i) { ++ m_file_mtime_box->setCountFromItemIndex(i, fileModifyTimeMap.value(i + 1)); ++ } ++ ++ QMap<int, int> fileSizeMap = currentPage()->getFileSizeCount(); ++ for (int i = 0; i < m_file_size_box->count(); ++i) { ++ m_file_size_box->setCountFromItemIndex(i, fileSizeMap.value(i + 1)); ++ } ++ ++ QMap<int, int> fileLabelMap = currentPage()->getFileLabelCount(); ++ for (int i = 0; i < m_file_label_box->count(); ++i) { ++ m_file_label_box->setCountFromItemIndex(i, fileLabelMap.value(i)); ++ } ++} ++ ++void TabWidget::clearAllMapsCount() ++{ ++ if(!currentPage()) ++ return; ++ ++ currentPage()->clearAllMapsCount(); ++ updateFilter(); ++} ++ + void TabWidget::updateSearchBar(bool showSearch) + { + qDebug() << "updateSearchBar:" <<showSearch; +- m_show_search_bar = showSearch; + if (showSearch && !qApp->property("tabletMode").toBool()) + { + m_search_title->show(); +@@ -1006,6 +933,8 @@ void TabWidget::updateSearchBar(bool showSearch) + updateSearchPathButton(getCurrentUri()); + m_jumpToComputer = false; + switchSearchPath(true); ++ m_search_type_box->show(); ++ updateSearchTypeShow(); + } + else + { +@@ -1013,51 +942,40 @@ void TabWidget::updateSearchBar(bool showSearch) + m_search_bar->hide(); + m_current_search->hide(); + m_home_search->hide(); ++ m_add_filter_button->setChecked(false); + m_add_filter_button->hide(); + m_jumpToComputer = false; ++ m_search_type_box->hide(); + m_search_bar_layout->setContentsMargins(10, 0, 10, 0); ++ updateAdvanceShow(false); + } + +- //if (m_search_bar_count >0) +- updateSearchList(); +- + if (! showSearch) + { + //exit advance search, clear search conditions + clearConditions(); + updateFilter(); +- } +- +- //9X0 changes, set default as true, fix bug#70916 +- enableSearchBar(true); +-} +- +-void TabWidget::updateButtons() +-{ +- //only one condition, set disabled +- //since ukui3.20,can delete all +-// if (m_search_bar_count ==1) +-// m_remove_button_list[0]->setDisabled(true); +-// else +-// m_remove_button_list[0]->setDisabled(false); + +- //limit total number to 10 +- if (m_search_bar_count >= 10) +- { +- for(int i=0;i<m_search_bar_count;i++) +- { +- m_add_button_list[i]->setDisabled(true); +- } +- m_add_filter_button->setDisabled(true); +- } +- else +- { +- for(int i=0;i<m_search_bar_count;i++) ++ /** ++ * @bug #242210: [File Manager] Enter the folder and click search, switch the search directory, ++ * repeat the second time you can not switch the search directory. ++ * ++ * Closing the search without resetting MainWindow's m_last_search_path ++ * to null prevents the search from being performed ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: %{CurrentDate::yyyy-MM-dd} ++ */ ++ MainWindow *mainWindow = dynamic_cast<MainWindow *>(this->topLevelWidget()); ++ if(mainWindow) + { +- m_add_button_list[i]->setDisabled(false); ++ mainWindow->clearLastSearchPath(); + } +- m_add_filter_button->setDisabled(false); ++ + } ++ ++ //9X0 changes, set default as true, fix bug#70916 ++ //enableSearchBar(true); + } + + void TabWidget::updateCurrentSearchPath() +@@ -1098,31 +1016,39 @@ void TabWidget::switchSearchPath(bool isCurrent) + + void TabWidget::updateSearchPathButton(const QString &uri) + { +- //search path not update +- //qDebug() << "updateSearchPathButton:" <<uri; +- if (uri.startsWith("search://")) +- return; + QString curUri = uri; +- if (uri == "") +- { ++ if (uri.startsWith("search:///")) { ++ // Extract the actual path from the search URI ++ curUri = Peony::SearchVFSUriParser::getSearchUriPath(uri); ++ } else if (uri.isEmpty()) { + curUri = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); +- if (! getCurrentUri().isNull()) ++ if (!getCurrentUri().isNull()) { + curUri = getCurrentUri(); ++ } ++ } ++ ++ // Get the current display name ++ auto displayName = Peony::FileUtils::getFileDisplayName(curUri); ++ if (displayName.contains("&")) { ++ displayName = Peony::FileUtils::handleSpecialSymbols(displayName); + } ++ ++ // Check if updates are needed ++ QString currentRealDisplayName = m_current_search->property("realDisplayName").toString(); ++ qDebug() << "curUri: " << curUri << " displayName: " << displayName << " currentRealDisplayName: " << currentRealDisplayName; ++ if (currentRealDisplayName == displayName) { ++ // If the display name has not changed, return directly ++ return; ++ } ++ ++ // Update file information + auto info = Peony::FileInfo::fromUri(curUri); +- m_search_button_info = info; ++ //m_search_button_info = info; + if (info.get()->isEmptyInfo()) { +- // TODO: use async method. + Peony::FileInfoJob j(info); + j.querySync(); + } +- auto iconName = Peony::FileUtils::getFileIconName(curUri); +- auto displayName = Peony::FileUtils::getFileDisplayName(curUri); +- qDebug() << "goToUri iconName:" <<iconName <<displayName<<curUri; + +- if (displayName.contains("&")) { +- displayName = Peony::FileUtils::handleSpecialSymbols(displayName); +- } + //elide text if it is too long, Use ElideMiddle mode to design + //related bug#155126, #185743 + m_current_search->setProperty("realDisplayName", displayName); +@@ -1131,57 +1057,6 @@ void TabWidget::updateSearchPathButton(const QString &uri) + m_current_search->adjustSize(); + } + +-void TabWidget::updateSearchList() +-{ +- m_show_search_list = !m_show_search_list; +- //if not show search bar, then don't show search list +- if (m_show_search_list && m_show_search_bar) +- { +- //m_search_more->setIcon(QIcon::fromTheme("go-up")); +- //first click to show advance serach +- if(m_search_bar_list.count() ==0) +- { +- //default can has 0 conditions in new design +- //addNewConditionBar(); +- return; +- } +- +- //already had a list,just set to show +- for(int i=0; i<m_search_bar_list.count(); i++) +- { +- m_conditions_list[i]->show(); +- m_link_label_list[i]->show(); +- if (m_conditions_list[i]->currentIndex()%4 < 3) +- m_classify_list[i]->show(); +- else +- m_input_list[i]->show(); +- m_search_bar_list[i]->show(); +- m_add_button_list[i]->show(); +- m_remove_button_list[i]->show(); +- m_layout_list[i]->setContentsMargins(10, 0, 10, 5); +- } +- } +- else +- { +- //hide search list +- //m_search_more->setIcon(QIcon::fromTheme("go-down")); +- for(int i=0; i<m_search_bar_list.count(); i++) +- { +- m_conditions_list[i]->hide(); +- //m_conditions_list[i]->setCurrentIndex(0); +- m_link_label_list[i]->hide(); +- m_classify_list[i]->hide(); +- m_classify_list[i]->setCurrentIndex(0); +- m_input_list[i]->hide(); +- m_input_list[i]->setText(""); +- m_search_bar_list[i]->hide(); +- m_add_button_list[i]->hide(); +- m_remove_button_list[i]->hide(); +- m_layout_list[i]->setContentsMargins(10, 0, 10, 0); +- } +- } +-} +- + Peony::DirectoryViewContainer *TabWidget::currentPage() + { + return qobject_cast<Peony::DirectoryViewContainer *>(m_stack->currentWidget()); +@@ -1432,7 +1307,7 @@ void TabWidget::addPage(const QString &uri, bool jumpTo) + } + + //Fix bug#132638, special # character use in symbolic link open fail issue +- if (realUri.contains("\#") && ! realUri.startsWith("filesafe:///")) ++ if (realUri.contains("\#") && ! realUri.startsWith("filesafe:///") && !realUri.startsWith("search:///")) + realUri = Peony::FileUtils::urlEncode(realUri); + + // fix #174653 +@@ -1440,6 +1315,10 @@ void TabWidget::addPage(const QString &uri, bool jumpTo) + realUri = "file:///"; + } + ++ if (!t.isEmpty()) { ++ realUri = t; ++ } ++ + //m_stack->addWidget(viewContainer); + viewContainer->goToUri(realUri, false, true); + +@@ -1676,34 +1555,42 @@ void TabWidget::updateFilter() + if(!currentPage()) + return; + currentPage()->updateFilter(); ++ m_status_bar->update(); + } + + void TabWidget::updateAdvanceConditions() + { + clearConditions(); + +- //get key list for proxy-filter +- //input name not show, must be empty +- QStringList keyList; +- for(int i=0; i<m_layout_list.count(); i++) +- { +- QString input = m_input_list[i]->text(); +- if(input != "" && ! keyList.contains(input)) +- { +- keyList.append(input); ++ for (int i = 0; i < m_conditions_list.size(); ++i) { ++ QList<int> selectIndexs = m_conditions_list.at(i)->getSelectItemsIndex(); ++ if (!selectIndexs.isEmpty()) { ++ for (int j = 0; j < selectIndexs.size(); j++) { ++ addFilterCondition(i, selectIndexs.at(j) + 1); ++ } + } +- else +- { +- addFilterCondition(m_conditions_list[i]->currentIndex(), m_classify_list[i]->currentIndex()); ++ } ++ ++ QStringList selectTexts = m_file_label_box->getSelectItemsText(); ++ if (!selectTexts.isEmpty()) { ++ QList<QColor> colors; ++ for (auto text : selectTexts) { ++ QColor tmpColor = FileLabelModel::getGlobalModel()->getLableColorFromLabelName(text); ++ if (tmpColor != Qt::transparent) { ++ colors.append(tmpColor); ++ } + } ++ setMutipleLabelConditions(selectTexts, colors); + } + ++ QStringList keyList = m_input_edit->text().split(","); + //update file name filter + for(auto key : keyList) + { + if(!currentPage()) + continue; +- currentPage()->addFileNameFilter(key); ++ if (key != "") ++ currentPage()->addFileNameFilter(key); + } + + updateFilter(); +@@ -1751,7 +1638,8 @@ void TabWidget::onViewDoubleClicked(const QString &uri) + if (info->uri().startsWith("trash://")) { + QMainWindow *w = Peony::PropertiesWindowFactoryPluginManager::getInstance()->create(QStringList()<<uri); + //auto w = new Peony::PropertiesWindow(QStringList()<<uri); +- w->show(); ++ //w->show(); ++ Peony::PropertiesWindowFactoryPluginManager::getInstance()->show(); + return; + } + if (info->isDir() || info->isVolume() || info->isVirtual()) { +@@ -1778,8 +1666,33 @@ void TabWidget::onViewDoubleClicked(const QString &uri) + + void TabWidget::changeCurrentIndex(int index) + { ++ //fix bug#291259, when index is -1 crash issue ++ if (index < 0 || index >= m_tab_bar->getCurrentUris().count()) { ++ return; ++ } ++ + m_tab_bar->setCurrentIndex(index); + m_stack->setCurrentIndex(index); ++ ++ QString uri = m_tab_bar->getCurrentUris().at(index); ++ /** ++ * @bug #292259: Various abnormal phenomena that occur when switching tabs in search mode ++ * ++ * separate handling of tab switching scenarios that deal with search states ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2025-01-06 ++ */ ++ bool isSearching = uri.startsWith("search:///"); ++ QString searchKey; ++ if (isSearching) { ++ searchKey = Peony::SearchVFSUriParser::getSearchUriNameRegexp(uri); ++ // emit a signal to update the search status of HeaderBar ++ Q_EMIT searchStateChanged(isSearching, searchKey); ++ } else { ++ closeSearch(); ++ } ++ + Q_EMIT currentIndexChanged(index); + Q_EMIT activePageChanged(); + } +@@ -1847,6 +1760,23 @@ void TabWidget::bindContainerSignal(Peony::DirectoryViewContainer *container) + connect(container, &Peony::DirectoryViewContainer::statusBarChanged, this, [=](){ + m_status_bar->update(); + }); ++ ++ connect(container, &Peony::DirectoryViewContainer::signal_updateTabPageTitle, this, [=](const QString& uri){ ++ for(int index = 0; index < m_stack->count(); index++){ ++ if(uri.startsWith("label:///") && uri == m_tab_bar->tabData(index).toString()){ ++ m_tab_bar->updateLocation(index, uri.toLocal8Bit()); ++ } ++ } ++ }); ++ ++ connect(container, &Peony::DirectoryViewContainer::signal_updateLocationBar, this, [=](const QString& uri){ ++ auto mainWindow = dynamic_cast<MainWindow *>(this->topLevelWidget()); ++ for(int index = 0; index < m_stack->count(); index++){ ++ if(uri.startsWith("label:///") && uri == m_tab_bar->tabData(index).toString()){ ++ mainWindow->updateHeaderBar(); ++ } ++ } ++ }); + } + + void TabWidget::updatePreviewPage() +@@ -1866,6 +1796,7 @@ void TabWidget::resizeEvent(QResizeEvent *e) + QMainWindow::resizeEvent(e); + updateTabBarGeometry(); + //updateStatusBarGeometry(); ++ updateSearchFilterHeight(); + } + + void TabWidget::updateTabBarGeometry() +diff --git a/src/control/tab-widget.h b/src/control/tab-widget.h +index fb7ca35..31458f4 100644 +--- a/src/control/tab-widget.h ++++ b/src/control/tab-widget.h +@@ -39,6 +39,9 @@ + #include "navigation-tab-bar.h" + #include "file-info.h" + #include "tab-status-bar.h" ++#include "multi-select-combobox.h" ++#include "flowlayout.h" ++#include "file-label-model.h" + + class NavigationTabBar; + class QStackedWidget; +@@ -51,6 +54,7 @@ namespace Peony { + class PreviewPageIface; + class DirectoryViewContainer; + class FileInfo; ++class MultiSelectComboBox; + } + + /*! +@@ -140,6 +144,9 @@ Q_SIGNALS: + void signal_itemAdded(const QString& uri);/* 新增文件(夹),item创建完成 */ + void updateItemsNum(); /*显示隐藏文件,更新项目个数*/ + ++ // add new signals to notify changes in search status ++ void searchStateChanged(bool isSearching, const QString &searchKey); ++ + public Q_SLOTS: + void setCurrentIndex(int index); + void setPreviewPage(Peony::PreviewPageIface *previewPage = nullptr); +@@ -193,16 +200,11 @@ public Q_SLOTS: + void updateTrashBarVisible(const QString &uri = ""); + void updateSearchPathButton(const QString &uri = ""); + void updateSearchBar(bool showSearch); +- void updateSearchList(); +- void updateButtons(); +- void addNewConditionBar(); +- void removeConditionBar(int index); + void searchUpdate(); + void searchChildUpdate(); + void browsePath(); + + void handleZoomLevel(int zoomLevel); +- void enableSearchBar(bool enable); + + void updateCurrentSearchPath(); + void switchSearchPath (bool isCurrent); +@@ -210,6 +212,12 @@ public Q_SLOTS: + /* 设备卸载、弹出后,其所在标签页跳转到计算机页(保护箱标签除外),其余标签页均关闭 */ + void slot_responseUnmounted(const QString &destUri, const QString &sourceUri); + void updateTabletModeValue(bool isTabletMode); ++ void updateSearchTypeShow(); ++ void updateFilterContent(const QString& key); ++ bool isSearchIndex(); ++ void setMutipleLabelConditions(QStringList names, QList<QColor> colors); ++ void updateMultiComboBoxCount(); ++ void clearAllMapsCount(); + + protected: + void changeCurrentIndex(int index); +@@ -224,8 +232,6 @@ protected: + + void initAdvanceSearch(); + +- QStringList getCurrentClassify(int rowCount); +- + void updatePreviewPageVisible(); + void updateStatusBarSliderState(); + void updatePreviewButtonStatus(bool status); +@@ -235,6 +241,8 @@ protected: + bool isMultFile(std::shared_ptr<Peony::FileInfo> info); + void setCondWidthWithFont(QComboBox *conditionCombox, int fontSize); + void setClassifyWidthWithFont(QComboBox *classifyCombox, int fontSize); ++ void updateSearchFilterHeight(); ++ void updateAdvanceShow(bool isVisible); + + private: + NavigationTabBar *m_tab_bar; +@@ -244,26 +252,14 @@ private: + QTreeView *m_treeView; + QStandardItemModel *m_model; + +- //QWidget *m_tab_bar_bg; +- QWidget *m_header_bar_bg; +- + QStackedWidget *m_stack; + +- PreviewPageButtonGroups *m_buttons; +- +- QToolBar *m_tool_bar; +- + Peony::PreviewPageIface *m_preview_page = nullptr; + QStackedWidget *m_preview_page_container; + +- QAction *m_current_preview_action = nullptr; +- QAction *m_preview_action = nullptr; +- QActionGroup *m_preview_action_group = nullptr; +- + QToolBar *m_trash_bar; + QToolBar *m_search_bar; + QVBoxLayout *m_top_layout; +- QHBoxLayout *m_header_bar_layout; + QHBoxLayout *m_trash_bar_layout; + QHBoxLayout *m_search_bar_layout; + QLabel *m_trash_label; +@@ -279,18 +275,23 @@ private: + QPushButton* m_home_search; + QPushButton* m_add_filter_button; + QSplitter* m_preview_splitter; ++ QComboBox* m_search_type_box; + + //use qlist for dynamic generated search conditions list +- QList<QHBoxLayout*> m_layout_list; +- QList<QComboBox*> m_conditions_list; ++ //QList<QHBoxLayout*> m_layout_list; + QList<QComboBox*> m_classify_list; +- QList<QLabel*> m_link_label_list; +- QList<QPushButton*> m_add_button_list; +- QList<QPushButton*> m_remove_button_list; +- QList<QToolBar*> m_search_bar_list; +- QList<QLineEdit*> m_input_list; +- QList<QSignalMapper*> m_add_mappper_list; +- QList<QSignalMapper*> m_remove_mapper_list; ++ ++ QList<Peony::MultiSelectComboBox *> m_conditions_list; ++ Peony::MultiSelectComboBox *m_file_type_box; ++ Peony::MultiSelectComboBox *m_file_mtime_box; ++ Peony::MultiSelectComboBox *m_file_size_box; ++ Peony::MultiSelectComboBox *m_file_label_box; ++ QLineEdit *m_input_edit; ++ QLabel *m_condition_label; ++ FlowLayout *m_search_filter_layout; ++ QWidget *m_filter_container; ++ QPushButton *m_conditions_clear_btn; ++ FileLabelModel *m_file_label_model; + + int m_search_bar_count = 0; + const int ELIDE_TEXT_LENGTH = 6; +@@ -298,8 +299,6 @@ private: + TabStatusBar *m_status_bar = nullptr; + + bool m_triggered_preview_page = false; +- bool m_show_search_list = false; +- bool m_show_search_bar = false; + bool m_search_child_flag = true; + bool m_isTabletMode = false; + bool m_jumpToComputer = false; +@@ -311,14 +310,11 @@ private: + + //advance search filter options + QStringList m_option_list = {tr("type"), tr("file size"), tr("modify time"), tr("name")}; +- QStringList m_file_type_list = {tr("all"), tr("file folder"), tr("image"), tr("video"), ++ QStringList m_file_type_list = {/*tr("all"), */tr("file folder"), tr("image"), tr("video"), + tr("text file"), tr("audio"), tr("wps file"), tr("others")}; +- QStringList m_file_mtime_list = {tr("all"), tr("today"), tr("yesterday"), tr("this week"), tr("last week"), tr("this month"), tr("last month"), tr("this year"), tr("last year")}; +- QStringList m_file_size_list = {tr("all"),tr("empty(0K)"), tr("tiny(0-16K)"), tr("small(16k-1M)"), tr("medium(1M-128M)"), tr("big(128M-1G)"),tr("large(1-4G)"),tr("great(>4G)")}; +- +- bool m_first_add_page = true; +- +- std::shared_ptr<Peony::FileInfo> m_search_button_info; ++ QStringList m_file_mtime_list = {/*tr("all"), */tr("today"), tr("yesterday"), tr("this week"), tr("last week"), tr("this month"), tr("last month"), tr("this year"), tr("last year")}; ++ QStringList m_file_size_list = {/*tr("all"),*/tr("empty(0K)"), tr("tiny(0-16K)"), tr("small(16k-1M)"), tr("medium(1M-128M)"), tr("big(128M-1G)"),tr("large(1-4G)"),tr("great(>4G)")}; ++ QStringList m_search_type_list = {tr("file name and content"), tr("file name")}; + + QWidget* m_parent = nullptr; + }; +diff --git a/src/peony-application.cpp b/src/peony-application.cpp +index da11924..f6a0661 100644 +--- a/src/peony-application.cpp ++++ b/src/peony-application.cpp +@@ -91,7 +91,7 @@ + #include <QThreadPool> + + #include "properties-window-factory-plugin-manager.h" +-//#include "properties-window.h" ++#include "properties-window.h" + + #include "complementary-style.h" + +@@ -332,6 +332,13 @@ QString PeonyApplication::getUriMessage(QStringList& strList) + + void PeonyApplication::parseCmd(quint32 id, QByteArray msg) + { ++ //story 28077, control start of peony ++ if (! Peony::GlobalSettings::getInstance()->getValue(ENABLE_START_PEONY).toBool()){ ++ qWarning() << "peony is diablsed to start"; ++ Peony::GlobalSettings::getInstance()->sendNotifyMessage(tr("Peony is disabled to start !")); ++ return; ++ } ++ + QCommandLineParser parser; + if (m_first_parse) { + parser.addHelpOption(); +@@ -431,8 +438,16 @@ void PeonyApplication::parseCmd(quint32 id, QByteArray msg) + qApp->setProperty("showProperties", true); + QMainWindow *window = Peony::PropertiesWindowFactoryPluginManager::getInstance()->create(uris); + //Peony::PropertiesWindow *window = new Peony::PropertiesWindow(uris); ++ Peony::PropertiesWindow *w = qobject_cast<Peony::PropertiesWindow *>(window); ++ if (w) { ++ if (Peony::GlobalSettings::getInstance()->isExist(SHOW_SHARE_PROPERTIES) && Peony::GlobalSettings::getInstance()->getValue(SHOW_SHARE_PROPERTIES).toBool()) { ++ w->setOpenTabPage("SharePage"); ++ Peony::GlobalSettings::getInstance()->setValue(SHOW_SHARE_PROPERTIES, false); ++ } ++ } + window->setAttribute(Qt::WA_DeleteOnClose); +- window->show(); ++ Peony::PropertiesWindowFactoryPluginManager::getInstance()->show(); ++ //window->show(); + KWindowSystem::raiseWindow(window->winId()); + if (KWindowSystem::activeWindow() != window->winId()) { + KWindowSystem::activateWindow(window->winId()); +diff --git a/src/src.pro b/src/src.pro +index b7efe1e..624da73 100644 +--- a/src/src.pro ++++ b/src/src.pro +@@ -21,6 +21,7 @@ include(windows/windows-peony.pri) + include(control/control.pri) + include(../plugin-iface/unstable/window-plugin-iface.pri) + #include(view/view.pri) ++include(../3rd-parties/layouts/layouts.pri) + DEFINES += QAPPLICATION_CLASS=QApplication + + PKGCONFIG +=gio-2.0 glib-2.0 gio-unix-2.0 gsettings-qt libcanberra libnotify udisks2 openssl dconf polkit-gobject-1 +diff --git a/src/windows/main-window.cpp b/src/windows/main-window.cpp +index 16d8cba..132632d 100644 +--- a/src/windows/main-window.cpp ++++ b/src/windows/main-window.cpp +@@ -433,6 +433,10 @@ void MainWindow::setShortCuts() + auto trashAction = new QAction(this); + trashAction->setShortcuts(QList<QKeySequence>()<<Qt::Key_Delete<<QKeySequence(Qt::CTRL + Qt::Key_D)); + connect(trashAction, &QAction::triggered, [=]() { ++ if (! Peony::GlobalSettings::getInstance()->getValue(ENABLE_SHORTCUT_KEYS).toBool()) { ++ return ; ++ } ++ + auto currentUri = getCurrentUri(); + if(currentUri.startsWith("search://")){ + currentUri = Peony::FileUtils::getActualDirFromSearchUri(currentUri); +@@ -476,6 +480,10 @@ void MainWindow::setShortCuts() + deleteAction->setShortcuts(QList<QKeySequence>()<<QKeySequence(Qt::SHIFT + Qt::Key_Delete)); + addAction(deleteAction); + connect(deleteAction, &QAction::triggered, [=]() { ++ if (! Peony::GlobalSettings::getInstance()->getValue(ENABLE_SHORTCUT_KEYS).toBool()) { ++ return ; ++ } ++ + auto currentUri = getCurrentUri(); + if(currentUri.startsWith("search://")){ + currentUri = Peony::FileUtils::getActualDirFromSearchUri(currentUri); +@@ -505,6 +513,8 @@ void MainWindow::setShortCuts() + connect(searchAction, &QAction::triggered, this, [=]() { + if (! m_is_search){ + m_is_search = true; ++ } ++ if (m_is_search) { + m_header_bar->startEdit(m_is_search); + } + }); +@@ -617,7 +627,8 @@ void MainWindow::setShortCuts() + QMainWindow *w = Peony::PropertiesWindowFactoryPluginManager::getInstance()->create(uris); + //Peony::PropertiesWindow *w = new Peony::PropertiesWindow(uris); + w->setAttribute(Qt::WA_DeleteOnClose); +- w->show(); ++ Peony::PropertiesWindowFactoryPluginManager::getInstance()->show(); ++ //w->show(); + }); + addAction(propertiesWindowAction); + +@@ -684,7 +695,24 @@ void MainWindow::setShortCuts() + auto remodelViewAction = new QAction(this); + remodelViewAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_0)); + connect(remodelViewAction, &QAction::triggered, this, [=]() { +- this->getCurrentPage()->setZoomLevelRequest(25); ++ /** ++ * @bug #313689: Press Ctrl+"0" on the keyboard in the icon view of the file manager to turn it into a list view ++ * ++ * Set the default zoom level to the minimum value in icon mode. ++ * If the maximum zoom level value is less than 25 in list mode, set it to the maximum zoom level value; otherwise, set it to 25 ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-12-27 ++ */ ++ auto view = this->getCurrentPage()->getView(); ++ int defaultZoomLevel = 25; ++ if (view->viewId() == "Icon View") { ++ defaultZoomLevel = view->minimumZoomLevel(); ++ } else if (view->viewId() == "List View" && view->maximumZoomLevel() < defaultZoomLevel) { ++ defaultZoomLevel = view->maximumZoomLevel(); ++ } ++ qDebug() << QString("View Type: %1 defaultZoomLevel: %2").arg(view->viewId()).arg(defaultZoomLevel); ++ this->getCurrentPage()->setZoomLevelRequest(defaultZoomLevel); + }); + addAction(remodelViewAction); + +@@ -734,6 +762,10 @@ void MainWindow::setShortCuts() + auto *selectAllAction = new QAction(this); + selectAllAction->setShortcut(QKeySequence::SelectAll); + connect(selectAllAction, &QAction::triggered, this, [=]() { ++ if (! Peony::GlobalSettings::getInstance()->getValue(ENABLE_SHORTCUT_KEYS).toBool()) { ++ return ; ++ } ++ + if (this->getCurrentPage()->getView()) + { + /// note: 通过getAllFileUris设置的全选效率过低,如果增加接口则会导致二进制兼容性问题 +@@ -750,6 +782,10 @@ void MainWindow::setShortCuts() + auto *copyAction = new QAction(this); + copyAction->setShortcut(QKeySequence::Copy); + connect(copyAction, &QAction::triggered, [=]() { ++ if (! Peony::GlobalSettings::getInstance()->getValue(ENABLE_SHORTCUT_KEYS).toBool()) { ++ return ; ++ } ++ + bool is_recent = false; + QStringList currentSelections = this->getCurrentSelections(); + if (!currentSelections.isEmpty()) +@@ -787,6 +823,10 @@ void MainWindow::setShortCuts() + auto *pasteAction = new QAction(this); + pasteAction->setShortcut(QKeySequence::Paste); + connect(pasteAction, &QAction::triggered, [=]() { ++ if (! Peony::GlobalSettings::getInstance()->getValue(ENABLE_SHORTCUT_KEYS).toBool()) { ++ return ; ++ } ++ + auto currentUri = getCurrentUri(); + if (currentUri.startsWith("trash://") || currentUri.startsWith("recent://") + || currentUri.startsWith("computer://") || currentUri.startsWith("favorite://") +@@ -840,6 +880,10 @@ void MainWindow::setShortCuts() + auto *cutAction = new QAction(this); + cutAction->setShortcut(QKeySequence::Cut); + connect(cutAction, &QAction::triggered, [=]() { ++ if (! Peony::GlobalSettings::getInstance()->getValue(ENABLE_SHORTCUT_KEYS).toBool()) { ++ return ; ++ } ++ + QStringList currentSelections = this->getCurrentSelections(); + if (!currentSelections.isEmpty()) { + // if (currentSelections.first().startsWith("trash://", Qt::CaseInsensitive)) { +@@ -854,15 +898,14 @@ void MainWindow::setShortCuts() + + QString currentUri = getCurrentUri(); + if(currentUri.startsWith("search://")){ +- currentUri = Peony::FileUtils::getActualDirFromSearchUri(currentUri); +- } +- auto info = Peony::FileInfo::fromUri(currentUri); +- if (!info->canWrite()) { +- if(getCurrentUri().startsWith("search://")){ +- auto selectInfo = Peony::FileInfo::fromUri(currentSelections.first()); +- if(!selectInfo->canWrite()) +- return; +- }else{ ++ auto selections = this->getCurrentSelections(); ++ bool canCut = Peony::FileUtils::isSearchFilesParentWriteable(selections, getCurrentUri().startsWith("search://")); ++ if(!canCut){ ++ return; ++ } ++ }else{ ++ auto info = Peony::FileInfo::fromUri(currentUri); ++ if (!info->canWrite()) { + return; + } + } +@@ -912,9 +955,10 @@ void MainWindow::createFolderOperation() + return; + auto opInfo = op->getOperationInfo(); + //auto targetUri = opInfo->target(); +- this->getCurrentPage()->getView()->clearIndexWidget(); ++ + //set a short time delay, fix bug#86070, select two folders +- QTimer::singleShot(10, this, [=](){ ++ QTimer::singleShot(300, this, [=](){ ++ this->getCurrentPage()->getView()->clearIndexWidget(); + this->editUri(opInfo->target()); + }); + }, Qt::BlockingQueuedConnection); +@@ -957,30 +1001,19 @@ void MainWindow::keyPressEvent(QKeyEvent *e) + files<<uri; + } + } ++ bool check = Peony::GlobalSettings::getInstance()->getValue(SHOW_IN_NEW_WINDOW).toBool(); + for (auto uri : dirs) { +- m_tab->addPage(uri); +- } +- +- QMap<QString, QStringList> fileMap; +- for (auto uri : files) { +- QString defaultAppName = Peony::FileLaunchManager::getDefaultAction(uri)->getAppInfoName(); +- QStringList list; +- if (fileMap.contains(defaultAppName)) { +- list = fileMap[defaultAppName]; +- list << uri; +- fileMap.insert(defaultAppName, list); ++ if (check) { ++ auto newWindow = dynamic_cast<QWidget *>(create(uri)); ++ newWindow->setAttribute(Qt::WA_DeleteOnClose); ++ newWindow->show(); + } else { +- list << uri; +- fileMap.insert(defaultAppName, list); ++ m_tab->addPage(uri); + } + } +- if(!fileMap.empty()) { +- QMap<QString, QStringList>::iterator iter = fileMap.begin(); +- while (iter != fileMap.end()) +- { +- Peony::FileLaunchManager::openAsync(iter.value()); +- iter++; +- } ++ ++ if (!files.isEmpty()) { ++ Peony::FileLaunchManager::openFilesByDefaultApplications(files); + } + } + } +@@ -1093,8 +1126,12 @@ bool MainWindow::currentViewSupportZoom() + + void MainWindow::maximizeOrRestore() + { +- if (m_tab->currentPage()) { +- m_tab->currentPage()->getView()->clearIndexWidget(); ++ if (getCurrentPage()) { ++ if (auto view = getCurrentPage()->getView()) { ++ QStringList uris = view->getSelections(); ++ view->clearIndexWidget(); ++ view->setSelections(uris); ++ } + } + if (!this->isMaximized()) { + this->showMaximized(); +@@ -1160,6 +1197,14 @@ void MainWindow::goToUri(const QString &uri, bool addHistory, bool force) + if (uri == "computer:///ukui-data-volume") { + realUri = "file:///data"; + } ++ ++ //story 28545, improve data block solution, when has no user file in /data, go to usershare ++ if ((uri == "file:///data" || realUri == "file:///data") && ++ Peony::FileUtils::isFileExsit("file:///data/usershare") && ++ ! Peony::FileUtils::isDataBlockHasUserFile()) { ++ realUri = "file:///data/usershare"; ++ } ++ + //process open symbolic link + auto info = Peony::FileInfo::fromUri(uri); + if (info->isSymbolLink() && info->symlinkTarget().length() >0 && +@@ -1182,7 +1227,7 @@ void MainWindow::goToUri(const QString &uri, bool addHistory, bool force) + } + + //Fix bug#132638, special # character use in symbolic link open fail issue +- if (realUri.contains("\#") && ! realUri.startsWith("filesafe:///")) ++ if (realUri.contains("\#") && ! realUri.startsWith("filesafe:///") && !realUri.startsWith("search:///")) + realUri = Peony::FileUtils::urlEncode(realUri); + + //if in search mode and key is not null, need quit search mode, bug#93528 +@@ -1190,7 +1235,7 @@ void MainWindow::goToUri(const QString &uri, bool addHistory, bool force) + if (! m_is_clear_serach && m_is_search && ! uri.startsWith("search://")) + { + m_is_search = false; +- m_header_bar->updateSearchRequest(m_is_search); ++ m_tab->closeSearch(); + } + + if (getCurrentUri() == realUri) { +@@ -1221,6 +1266,10 @@ void MainWindow::updateSearch(const QString &uri, const QString &key, bool updat + needUpdate = true; + } + ++ if (m_last_key != key && key != "") { ++ m_tab->clearAllMapsCount(); ++ } ++ + if (updateKey) + { + needUpdate = true; +@@ -1236,6 +1285,7 @@ void MainWindow::updateSearch(const QString &uri, const QString &key, bool updat + goToUri(m_last_search_path, true); + m_is_clear_serach = false; + m_tab->m_status_bar->updateSearchProgress(false); ++ m_tab->updateFilterContent(""); + m_searching = false; + } + else +@@ -1261,6 +1311,7 @@ void MainWindow::updateSearch(const QString &uri, const QString &key, bool updat + //qDebug() << "updateSearch targetUri:" <<targetUri; + goToUri(targetUri, true); + m_tab->m_status_bar->updateSearchProgress(true); ++ m_tab->updateFilterContent(m_last_key); + m_searching = true; + } + } +@@ -1304,6 +1355,11 @@ void MainWindow::beginSwitchView(const QString &viewId) + + void MainWindow::refresh() + { ++ if (getCurrentPage()) { ++ if (getCurrentPage()->getView()) { ++ this->getCurrentPage()->getView()->setDirectoryUri(getCurrentUri()); ++ } ++ } + locationChangeStart(); + m_tab->refresh(); + //fix refresh clear copy files issue, link to bug#109247 +@@ -1440,6 +1496,10 @@ QString MainWindow::getLastSearchKey() + return m_last_key; + } + ++void MainWindow::clearLastSearchPath() ++{ ++ m_last_search_path = ""; ++} + + void MainWindow::resizeEvent(QResizeEvent *e) + { +@@ -1681,6 +1741,16 @@ void MainWindow::initUI(const QString &uri) + } + }); + ++ connect(m_tab, &TabWidget::searchStateChanged, this, [this](bool isSearching, const QString &searchKey) { ++ if (isSearching) { ++ m_header_bar->startEdit(true); // 进入搜索状态 ++ m_header_bar->m_searchWidget->setSearchMode(true); ++ m_header_bar->m_searchWidget->setSearchText(searchKey); ++ } else { ++ m_header_bar->m_searchWidget->clearSearchBox(); ++ } ++ }); ++ + //SideBar + auto sideBarFactory = Peony::SideBarFactoryManager::getInstance()->getFactoryFromPlatformName(); + if (!sideBarFactory) { +@@ -1766,10 +1836,11 @@ void MainWindow::initUI(const QString &uri) + + }); + connect(m_tab, &TabWidget::closeWindowRequest, this, &QWidget::close); +- connect(m_header_bar, &HeaderBar::updateSearchRequest, m_tab, &TabWidget::updateSearchBar); +- connect(m_header_bar, &HeaderBar::updateSearchRequest, this, [=](bool showSearch){ ++ connect(m_header_bar->m_searchWidget, &Peony::SearchWidget::updateSearchRequest, m_tab, &TabWidget::updateSearchBar); ++ connect(m_header_bar->m_searchWidget, &Peony::SearchWidget::updateSearchRequest, this, [=](bool showSearch){ + m_is_search = showSearch; + }); ++ + //connect(m_header_bar, &HeaderBar::updateSearchRequest, this, &MainWindow::updateSearchStatus); + connect(m_header_bar->m_searchWidget, &Peony::SearchWidget::updateSearch, this, &MainWindow::updateSearch); + +@@ -1800,7 +1871,7 @@ void MainWindow::initUI(const QString &uri) + + //bind signals + connect(m_tab, &TabWidget::searchRecursiveChanged, m_header_bar, &HeaderBar::updateSearchRecursive); +- connect(m_tab, &TabWidget::closeSearch, m_header_bar, &HeaderBar::closeSearch); ++ connect(m_tab, &TabWidget::closeSearch, m_header_bar->m_searchWidget, &Peony::SearchWidget::closeSearch); + connect(m_tab, &TabWidget::closeSearch, this, [=](){ + this->updateSearchStatus(false); + }); +@@ -1848,6 +1919,10 @@ void MainWindow::initUI(const QString &uri) + m_tab->setUpdatesEnabled(true); + } + }); ++ //fix bug#250273,right menu not show complete issue ++ QScreen *screen=qApp->primaryScreen(); ++ QRect geometry = screen->availableGeometry(); ++ menu.setMaximumHeight(geometry.height()); + menu.exec(pos); + m_tab->setUpdatesEnabled(true);//end + m_uris_to_edit = menu.urisToEdit(); +diff --git a/src/windows/main-window.h b/src/windows/main-window.h +index 9636b93..cf0ce74 100644 +--- a/src/windows/main-window.h ++++ b/src/windows/main-window.h +@@ -156,6 +156,7 @@ public Q_SLOTS: + + void setCurrentViewZoomLevel(int zoomLevel); + QString getLastSearchKey(); ++ void clearLastSearchPath(); + void updateTabletModeValue(bool isTabletMode); + + void updateSearchStatus(bool isSearching); +diff --git a/stable/ukui3/properties-window/properties-window-factory.cpp b/stable/ukui3/properties-window/properties-window-factory.cpp +index 9e23739..31a4389 100644 +--- a/stable/ukui3/properties-window/properties-window-factory.cpp ++++ b/stable/ukui3/properties-window/properties-window-factory.cpp +@@ -39,10 +39,10 @@ StablePropertiesWindowFactory *StablePropertiesWindowFactory::getInstance() + return global_instance; + } + +-QMainWindow *StablePropertiesWindowFactory::create(const QStringList &uris) ++QMainWindow *StablePropertiesWindowFactory::create(const QStringList &uris, QWidget *parent) + { +- auto window = new StablePropertiesWindow(uris); +- return window; ++ m_window = new StablePropertiesWindow(uris, parent); ++ return m_window; + } + + void StablePropertiesWindowFactory::closeFactory() +@@ -61,3 +61,10 @@ bool StablePropertiesWindowFactory::unregisterFactory(QObject *factory) + PropertiesWindowTabPagePluginIface *Iface = dynamic_cast<PropertiesWindowTabPagePluginIface*>(factory); + return PropertiesWindowPluginManager::getInstance()->unregisterFactory(Iface); + } ++ ++void StablePropertiesWindowFactory::show() ++{ ++ if (m_window) { ++ m_window->show(); ++ } ++} +diff --git a/stable/ukui3/properties-window/properties-window-factory.h b/stable/ukui3/properties-window/properties-window-factory.h +index 49fcd05..3fa715c 100644 +--- a/stable/ukui3/properties-window/properties-window-factory.h ++++ b/stable/ukui3/properties-window/properties-window-factory.h +@@ -32,6 +32,7 @@ + namespace Peony { + + class PropertiesWindowTabPagePluginIface; ++class StablePropertiesWindow; + + /*! + * \brief The StablePropertiesWindowFactory class +@@ -69,7 +70,7 @@ public: + return true; + } + +- QMainWindow *create(const QStringList &uris); ++ QMainWindow *create(const QStringList &uris, QWidget *parent); + void closeFactory(); + + explicit StablePropertiesWindowFactory(QObject *parent = nullptr); +@@ -77,11 +78,11 @@ public: + + bool registerFactory(QObject *factory); + bool unregisterFactory(QObject *factory); +-// Peony::PropertiesWindowPluginManager *getManager() { +-// return m_manager; +-// } +-//private: +-// Peony::PropertiesWindowPluginManager *m_manager = nullptr; ++ ++ void show(); ++ ++private: ++ StablePropertiesWindow* m_window = nullptr; + }; + + } +diff --git a/stable/ukui3/windows/main-window.cpp b/stable/ukui3/windows/main-window.cpp +index 892051e..39c3f00 100644 +--- a/stable/ukui3/windows/main-window.cpp ++++ b/stable/ukui3/windows/main-window.cpp +@@ -568,7 +568,8 @@ void MainWindow::setShortCuts() + QMainWindow *w = Peony::PropertiesWindowFactoryPluginManager::getInstance()->create(uris); + //Peony::PropertiesWindow *w = new Peony::PropertiesWindow(uris); + w->setAttribute(Qt::WA_DeleteOnClose); +- w->show(); ++ Peony::PropertiesWindowFactoryPluginManager::getInstance()->show(); ++ //w->show(); + }); + addAction(propertiesWindowAction); + +diff --git a/tests/kt-gtest/file-manager-operation/file-manager-operation.cpp b/tests/kt-gtest/file-manager-operation/file-manager-operation.cpp +new file mode 100644 +index 0000000..c1e2748 +--- /dev/null ++++ b/tests/kt-gtest/file-manager-operation/file-manager-operation.cpp +@@ -0,0 +1,167 @@ ++/* ++ * Peony-Qt ++ * ++ * Copyright (C) 2024, Tianjin KYLIN Information Technology Co., Ltd. ++ * ++ * 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 3 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, see <https://www.gnu.org/licenses/>. ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-08-23 14:52 ++ */ ++ ++#include <gtest/gtest.h> ++#include <gmock/gmock.h> ++#include <gmock/gmock-matchers.h> ++ ++#include <QDebug> ++#include <QDir> ++#include <QSignalSpy> ++ ++#include "directory-view-container.h" ++#include "directory-view-factory-manager.h" ++#include "file-info.h" ++#include "file-utils.h" ++ ++ ++using namespace testing; ++using namespace Peony; ++ ++// custom operator<< function prints a QString ++std::ostream& operator<<(std::ostream& os, const QString& str) { ++ os << str.toStdString(); ++ return os; ++} ++ ++class MockDirectoryViewContainer : public DirectoryViewContainer ++{ ++public: ++ // add the constructor and pass the parent to the base class constructor. ++ explicit MockDirectoryViewContainer(QWidget* parent = nullptr) : DirectoryViewContainer(parent) {} ++}; ++ ++class BackPreDirTest : public Test ++{ ++protected: ++ void SetUp() override ++ { ++ // get the current user's home directory ++ QString homeDir = QDir::homePath(); ++ ++ testDir = QDir(homeDir + "/_xxx_test"); ++ if (testDir.exists()) { ++ if (!testDir.removeRecursively()) { ++ qDebug() << "Failed to remove directory: " << testDir.path(); ++ return; ++ } ++ } ++ ++ // create the “_xxx_test” directory ++ if (!testDir.mkdir(homeDir + "/_xxx_test")) { ++ qDebug() << "Failed to create directory: " << testDir.path(); ++ return; ++ } ++ ++ // create 100 directories under the “_xxx_test” directory. ++ for (int i = 1; i <= 100; i++) { ++ QString dirName = QString("dir_%1").arg(i, 3, 10, QChar('0')); // naming directories with 3 digits ++ QDir dir(testDir.path() + "/" + dirName); ++ if (!dir.exists()) { ++ if (!dir.mkdir(dir.path())) { ++ qDebug() << "Failed to create directory: " << dir.path(); ++ return; ++ } else { ++ //qDebug() << "Created directory: " << dir.path(); ++ } ++ } ++ ++ if (i == 1) ++ oneDir = dir.absolutePath(); ++ else if (i == 90) ++ twoDir = dir.absolutePath(); ++ } ++ ++ parent = new QWidget; ++ mock_container = new MockDirectoryViewContainer(parent); ++ } ++ ++ void TearDown() override { ++ // delete the “_xxx_test” directory at the end of a test case ++ if (!testDir.removeRecursively()) { ++ FAIL() << "Failed to remove directory: " << testDir.path(); ++ } ++ ++ delete mock_container; ++ } ++ ++ ++protected: ++ QDir testDir; ++ QString oneDir; ++ QString twoDir; ++ QWidget* parent = nullptr; ++ MockDirectoryViewContainer* mock_container = nullptr; ++}; ++ ++TEST_F(BackPreDirTest, init) { ++ // calculate the total number of folders created ++ auto dirCount = testDir.entryList(QDir::NoDotAndDotDot | QDir::Dirs).count(); ++ ASSERT_EQ(dirCount, 100) << "100 folders were not created during the initialization phase"; ++} ++ ++TEST_F(BackPreDirTest, GoBack_CannotGoBack) { ++ // testcase 1: goBack() doesn't do anything when it can't go back ++ //EXPECT_CALL(*mock_container, updateWindowLocationRequest(_, _)).Times(0); ++ mock_container->goBack(); ++ ASSERT_FALSE(mock_container->canGoBack()); ++ ASSERT_FALSE(mock_container->canGoForward()); ++} ++ ++TEST_F(BackPreDirTest, GoBack_CanGoBack) { ++ // testcase 2: goBack() correctly updates history, sets properties, and sends signals when it can go back ++ // access ~/_xxx_test/dir_001 ++ mock_container->goToUri(oneDir, true); ++ ASSERT_FALSE(mock_container->canGoForward()); ++ ++ // access ~/_xxx_test/dir_090 ++ mock_container->goToUri(twoDir, true); ++ ++ ASSERT_EQ(mock_container->getCurrentUri(), twoDir); ++ ASSERT_TRUE(mock_container->canGoBack()); ++ ++ // create a QSignalSpy to listen for the updateWindowLocationRequest signal. ++ QSignalSpy spy(mock_container, SIGNAL(updateWindowLocationRequest(const QString&, bool, bool))); ++ ++ qDebug() << "Before getBackList(): " << mock_container->getBackList(); ++ qDebug() << "Before getForwardList(): " << mock_container->getForwardList(); ++ ++ mock_container->goBack(); ++ ++ qDebug() << "After getBackList(): " << mock_container->getBackList(); ++ qDebug() << "After getForwardList(): " << mock_container->getForwardList(); ++ ++ ASSERT_TRUE(mock_container->canGoForward()); ++ ASSERT_FALSE(mock_container->canGoBack()); ++ ASSERT_EQ(mock_container->property("mSelectPreviousFolder").toBool(), true); ++ ++ // assertion signal is triggered ++ ASSERT_EQ(spy.count(), 1); ++ // check signal parameters ++ qDebug() << "oneDir:" << oneDir; ++ qDebug() << "Signal arguments:" << spy.at(0); ++ QList<QVariant> arguments = spy.takeFirst(); ++ ASSERT_EQ(arguments.at(0).toString(), ""); ++ ASSERT_FALSE(arguments.at(1).toBool()); ++ ++} ++ +diff --git a/tests/kt-gtest/file-manager-operation/file-manager-operation.pro b/tests/kt-gtest/file-manager-operation/file-manager-operation.pro +new file mode 100644 +index 0000000..3ba460a +--- /dev/null ++++ b/tests/kt-gtest/file-manager-operation/file-manager-operation.pro +@@ -0,0 +1,18 @@ ++include($$PWD/../test-utils.pri) ++QT += core testlib gui widgets ++TEMPLATE = app ++CONFIG += c++11 testcase link_pkgconfig no_testcase_installs ++ ++LIB_PEONY_PATH = $$PWD/../../../libpeony-qt ++ ++INCLUDEPATH += $$LIB_PEONY_PATH \ ++ $$LIB_PEONY_PATH/controls/directory-view \ ++ $$LIB_PEONY_PATH/controls/directory-view/directory-view-factory \ ++ $$LIB_PEONY_PATH/model \ ++ $$LIB_PEONY_PATH/controls/directory-view-factory \ ++ ++SOURCES += main.cpp \ ++ file-manager-operation.cpp ++ ++LIBS += -lpeony -lgio-2.0 -lglib-2.0 ++PKGCONFIG += gio-unix-2.0 gsettings-qt +diff --git a/tests/kt-gtest/file-manager-operation/main.cpp b/tests/kt-gtest/file-manager-operation/main.cpp +new file mode 100644 +index 0000000..1cb8576 +--- /dev/null ++++ b/tests/kt-gtest/file-manager-operation/main.cpp +@@ -0,0 +1,8 @@ ++#include <gtest/gtest.h> ++#include <QApplication> ++int main(int argc, char *argv[]) ++{ ++ QApplication app(argc, argv); ++ ::testing::InitGoogleTest(&argc, argv); ++ return RUN_ALL_TESTS(); ++} +diff --git a/tests/kt-gtest/kt-desktop-gtest/kt-desktop-gtest.pro b/tests/kt-gtest/kt-desktop-gtest/kt-desktop-gtest.pro +new file mode 100644 +index 0000000..fe26c97 +--- /dev/null ++++ b/tests/kt-gtest/kt-desktop-gtest/kt-desktop-gtest.pro +@@ -0,0 +1,21 @@ ++include($$PWD/../test-utils.pri) ++QT += core testlib gui widgets dbus concurrent KWindowSystem KWaylandClient ++greaterThan(QT_MAJOR_VERSION, 4): QT += widgets ++TEMPLATE = app ++CONFIG += c++11 testcase link_pkgconfig no_testcase_installs ++CONFIG += no_keywords ++ ++include($$PWD/../../../common.pri) ++include($$PWD/../../../libpeony-qt/libpeony-qt-header.pri) ++include($$PWD/../../../3rd-parties/SingleApplication/singleapplication.pri) ++include($$PWD/../../../3rd-parties/qtsingleapplication/qtsingleapplication.pri) ++LIB_PEONY_PATH = $$PWD/../../../libpeony-qt ++DESKTOP_PEONY_PATH = $$PWD/../../../peony-qt-desktop ++ ++INCLUDEPATH += $$LIB_PEONY_PATH ++ ++SOURCES += main.cpp \ ++ tst_desktoptest.cpp ++#包含的库和包 ++LIBS += -lpeony -lX11 ++PKGCONFIG += gio-unix-2.0 gio-2.0 glib-2.0 gsettings-qt libcanberra wayland-client dconf +diff --git a/tests/kt-gtest/kt-desktop-gtest/main.cpp b/tests/kt-gtest/kt-desktop-gtest/main.cpp +new file mode 100644 +index 0000000..9c69768 +--- /dev/null ++++ b/tests/kt-gtest/kt-desktop-gtest/main.cpp +@@ -0,0 +1,9 @@ ++#include "tst_desktoptest.cpp" ++ ++#include <gtest/gtest.h> ++ ++int main(int argc, char *argv[]) ++{ ++ ::testing::InitGoogleTest(&argc, argv); ++ return RUN_ALL_TESTS(); ++} +diff --git a/tests/kt-gtest/kt-desktop-gtest/tst_desktoptest.cpp b/tests/kt-gtest/kt-desktop-gtest/tst_desktoptest.cpp +new file mode 100644 +index 0000000..7a67fa9 +--- /dev/null ++++ b/tests/kt-gtest/kt-desktop-gtest/tst_desktoptest.cpp +@@ -0,0 +1,25 @@ ++#include <gtest/gtest.h> ++#include <QWidget> ++#include "stubext.h" ++#include "global-settings.h" ++ ++using namespace testing; ++ ++class DesktopItemProxyModelTest : public testing::Test ++{ ++public: ++ ++private: ++ //打桩声明 ++ stub_ext::StubExt stub; ++ ++}; ++class GlobalSettingsTest : public testing::Test ++{ ++public: ++ Peony::GlobalSettings* settings = Peony::GlobalSettings::getInstance(); ++private: ++ //打桩声明 ++ stub_ext::StubExt stub; ++ ++}; +diff --git a/tests/kt-gtest/kt-gtest.pro b/tests/kt-gtest/kt-gtest.pro +new file mode 100644 +index 0000000..348fbf0 +--- /dev/null ++++ b/tests/kt-gtest/kt-gtest.pro +@@ -0,0 +1,6 @@ ++TEMPLATE = subdirs ++ ++SUBDIRS += \ ++ kt-libpeony-gtest \ ++ kt-desktop-gtest \ ++ kt-src-gtest +diff --git a/tests/kt-gtest/kt-libpeony-gtest/kt-core-gtest/kt-core-gtest.pro b/tests/kt-gtest/kt-libpeony-gtest/kt-core-gtest/kt-core-gtest.pro +new file mode 100644 +index 0000000..7328b8c +--- /dev/null ++++ b/tests/kt-gtest/kt-libpeony-gtest/kt-core-gtest/kt-core-gtest.pro +@@ -0,0 +1,7 @@ ++include($$PWD/../kt-libpeony-gtest.pri) ++include($$PWD/../../test-utils.pri) ++ ++TEMPLATE = app ++# 源文件 ++SOURCES += main.cpp \ ++ tst_core_gtest.cpp \ +diff --git a/tests/kt-gtest/kt-libpeony-gtest/kt-core-gtest/main.cpp b/tests/kt-gtest/kt-libpeony-gtest/kt-core-gtest/main.cpp +new file mode 100644 +index 0000000..1cb8576 +--- /dev/null ++++ b/tests/kt-gtest/kt-libpeony-gtest/kt-core-gtest/main.cpp +@@ -0,0 +1,8 @@ ++#include <gtest/gtest.h> ++#include <QApplication> ++int main(int argc, char *argv[]) ++{ ++ QApplication app(argc, argv); ++ ::testing::InitGoogleTest(&argc, argv); ++ return RUN_ALL_TESTS(); ++} +diff --git a/tests/kt-gtest/kt-libpeony-gtest/kt-core-gtest/tst_core_gtest.cpp b/tests/kt-gtest/kt-libpeony-gtest/kt-core-gtest/tst_core_gtest.cpp +new file mode 100644 +index 0000000..5906d65 +--- /dev/null ++++ b/tests/kt-gtest/kt-libpeony-gtest/kt-core-gtest/tst_core_gtest.cpp +@@ -0,0 +1,84 @@ ++#include <gtest/gtest.h> ++#include <qtest.h> ++#include "stubext.h" ++ ++#include "file-info.h" ++#include "file-info-job.h" ++#include "file-infos-job.h" ++#include "file-enumerator.h" ++#include "file-watcher.h" ++ ++#include <QEventLoop> ++#include <QFile> ++#include <QDir> ++#include <QSignalSpy> ++ ++using namespace testing; ++ ++class coreTest : public testing::Test ++{ ++public: ++ ++private: ++ //打桩声明 ++ stub_ext::StubExt stub; ++ ++}; ++ ++using namespace Peony; ++ ++TEST_F(coreTest, testFileInfo) ++{ ++ auto info1 = FileInfo::fromUri("file:///"); ++ auto info2 = FileInfo::fromPath("/"); ++ g_autoptr (GFile) tmp_file = g_file_new_for_path("/"); ++ auto info3 = FileInfo::fromGFile(tmp_file); ++ ++ ASSERT_EQ(info1, info2); ++ ASSERT_EQ(info1, info3); ++ ASSERT_EQ(info2, info3); ++} ++ ++TEST_F(coreTest, testFileInfoJobSync) ++{ ++ auto info = FileInfo::fromUri("file:///"); ++ ++ FileInfoJob job("file:///"); ++ job.querySync(); ++ ++ std::vector<std::shared_ptr<FileInfo> >infos; ++ infos.push_back(info); ++ FileInfosJob jobs(infos); ++ jobs.batchQuerySync(); ++ ++ ASSERT_TRUE(!info->isEmptyInfo()); ++ ++ auto tmp = FileInfo::fromUri("file:///"); ++ ASSERT_EQ(info, tmp); ++} ++ ++TEST_F(coreTest, testFileEnumeratorSync) ++{ ++ FileEnumerator e; ++ e.setEnumerateDirectory("file:///"); ++ e.setEnumerateWithInfoJob(); ++ e.enumerateSync(); ++ ++ auto files = e.getChildrenUris(); ++ ASSERT_TRUE(!files.isEmpty()); ++} ++ ++TEST_F(coreTest, testFileWatcher) ++{ ++ FileWatcher w("file:///tmp"); ++ w.setMonitorChildrenChange(); ++ w.startMonitor(); ++ QSignalSpy spy(&w, &FileWatcher::fileCreated); ++ QDir dir("/tmp"); ++ if (!dir.mkdir("peony-core-test")) { ++ return; ++ } ++ spy.wait(); ++ dir.remove("peony-core-test"); ++ ASSERT_TRUE(spy.isValid()); ++} +diff --git a/tests/kt-gtest/kt-libpeony-gtest/kt-libpeony-gtest.pri b/tests/kt-gtest/kt-libpeony-gtest/kt-libpeony-gtest.pri +new file mode 100644 +index 0000000..82eedc6 +--- /dev/null ++++ b/tests/kt-gtest/kt-libpeony-gtest/kt-libpeony-gtest.pri +@@ -0,0 +1,47 @@ ++# 打桩和项目代码包含目录(需要根据项目工程位置调配) ++LIB_PEONY_PATH = $$PWD/../../../libpeony-qt ++LIBS += -L$$PWD/../../../libpeony-qt/ -lpeony ++# 包含目录 ++INCLUDEPATH += $$LIB_PEONY_PATH ++include($$LIB_PEONY_PATH/../common.pri) ++#include($$LIB_PEONY_PATH/libpeony-qt.pri) ++ ++QT += core testlib gui widgets concurrent xml KWindowSystem dbus x11extras ++#CONFIG += create_pc create_prl no_install_prl ++CONFIG += c++11 testcase link_pkgconfig no_testcase_installs no_keywords hide_symbols lrelease ++DEFINES += PEONYCORE_LIBRARY ++contains(DEFINES, KY_FILE_DIALOG) { ++ PKGCONFIG += kysdk-qtwidgets ++} ++ ++contains(DEFINES, KY_SDK_SYSINFO) { ++ PKGCONFIG += kysdk-sysinfo ++} ++ ++contains(DEFINES, KY_SDK_QT_WIDGETS) { ++ PKGCONFIG += kysdk-qtwidgets ++} ++ ++contains(DEFINES, KY_SDK_WAYLANDHELPER) { ++ PKGCONFIG += kysdk-waylandhelper ++} ++ ++contains(DEFINES, KY_SDK_SYSINFO) { ++ PKGCONFIG += kysdk-sysinfo ++} ++ ++schemes.files += org.ukui.peony.settings.gschema.xml ++schemes.path = /usr/share/glib-2.0/schemas/ ++ ++PLUGIN_INSTALL_DIRS = $$[QT_INSTALL_LIBS]/peony-extensions ++DEFINES += PLUGIN_INSTALL_DIRS='\\"$${PLUGIN_INSTALL_DIRS}\\"' ++ ++PROPERTIES_WINDOW_PLUGIN_INSTALL_DIRS = $$[QT_INSTALL_LIBS]/peony-properties-window ++DEFINES += PROPERTIES_WINDOW_PLUGIN_INSTALL_DIRS='\\"$${PROPERTIES_WINDOW_PLUGIN_INSTALL_DIRS}\\"' ++ ++QMAKE_CXXFLAGS += -execution-charset:utf-8 ++ ++#包含的库和包 ++LIBS +=-lgio-2.0 -lglib-2.0 ++PKGCONFIG += gio-unix-2.0 poppler-qt5 gsettings-qt udisks2 libnotify libcanberra openssl x11-xcb dconf ++ +diff --git a/tests/kt-gtest/kt-libpeony-gtest/kt-libpeony-gtest.pro b/tests/kt-gtest/kt-libpeony-gtest/kt-libpeony-gtest.pro +new file mode 100644 +index 0000000..276b62f +--- /dev/null ++++ b/tests/kt-gtest/kt-libpeony-gtest/kt-libpeony-gtest.pro +@@ -0,0 +1,8 @@ ++TEMPLATE = subdirs ++ ++SUBDIRS += \ ++ kt-operation-gtest \ ++ kt-core-gtest \ ++ kt-model-gtest \ ++ kt-plugin-iface-gtest ++ +diff --git a/tests/kt-gtest/kt-libpeony-gtest/kt-model-gtest/kt-model-gtest.pro b/tests/kt-gtest/kt-libpeony-gtest/kt-model-gtest/kt-model-gtest.pro +new file mode 100644 +index 0000000..5ff0320 +--- /dev/null ++++ b/tests/kt-gtest/kt-libpeony-gtest/kt-model-gtest/kt-model-gtest.pro +@@ -0,0 +1,8 @@ ++include($$PWD/../kt-libpeony-gtest.pri) ++include($$PWD/../../test-utils.pri) ++ ++INCLUDEPATH += $$PWD/../../../../libpeony-qt/model ++ ++# 源文件 ++SOURCES += main.cpp \ ++ tst_model.cpp \ +diff --git a/tests/kt-gtest/kt-libpeony-gtest/kt-model-gtest/main.cpp b/tests/kt-gtest/kt-libpeony-gtest/kt-model-gtest/main.cpp +new file mode 100644 +index 0000000..1cb8576 +--- /dev/null ++++ b/tests/kt-gtest/kt-libpeony-gtest/kt-model-gtest/main.cpp +@@ -0,0 +1,8 @@ ++#include <gtest/gtest.h> ++#include <QApplication> ++int main(int argc, char *argv[]) ++{ ++ QApplication app(argc, argv); ++ ::testing::InitGoogleTest(&argc, argv); ++ return RUN_ALL_TESTS(); ++} +diff --git a/tests/kt-gtest/kt-libpeony-gtest/kt-model-gtest/tst_model.cpp b/tests/kt-gtest/kt-libpeony-gtest/kt-model-gtest/tst_model.cpp +new file mode 100644 +index 0000000..559e38e +--- /dev/null ++++ b/tests/kt-gtest/kt-libpeony-gtest/kt-model-gtest/tst_model.cpp +@@ -0,0 +1,98 @@ ++#include <gtest/gtest.h> ++#include <qtest.h> ++#include "stubext.h" ++ ++using namespace testing; ++ ++class modelTest : public testing::Test ++{ ++public: ++ ++private: ++ //打桩声明 ++ stub_ext::StubExt stub; ++}; ++ ++ ++#include "model/file-item-model.h" ++#include "global-settings.h" ++class FileItemModelTest : public testing::Test ++{ ++public: ++ Peony::FileItemModel* fileItemModel = new Peony::FileItemModel; ++private: ++ //打桩声明 ++ stub_ext::StubExt stub; ++}; ++ ++//测试类 ++TEST_F(FileItemModelTest, columnCount) ++{ ++ /* 该函数用于验证列表视图页面列数(常规目录有四列,回收站有5列) */ ++ fileItemModel->setRootUri("file:///home/kylin"); ++ int columnCount = fileItemModel->columnCount(QModelIndex()); ++ EXPECT_EQ(columnCount, 4); ++ ++ fileItemModel->setRootUri("trash:///"); ++ columnCount = fileItemModel->columnCount(QModelIndex()); ++ EXPECT_EQ(columnCount, 5); ++} ++ ++TEST_F(FileItemModelTest, headerData) ++{ ++ /* 该函数用于验证列表视图页面列头的各列名称 */ ++ fileItemModel->setRootUri("file:///home/kylin"); ++ QString headerColumeName = fileItemModel->headerData(0, Qt::Orientation::Horizontal, Qt::DisplayRole).toString(); ++ EXPECT_EQ(headerColumeName, QObject::tr("File Name")); ++ if(Peony::GlobalSettings::getInstance()->getValue(SHOW_CREATE_TIME).toBool()){ ++ headerColumeName = fileItemModel->headerData(1, Qt::Orientation::Horizontal, Qt::DisplayRole).toString(); ++ EXPECT_EQ(headerColumeName, QObject::tr("Create Date")); ++ }else{ ++ headerColumeName = fileItemModel->headerData(1, Qt::Orientation::Horizontal, Qt::DisplayRole).toString(); ++ EXPECT_EQ(headerColumeName, QObject::tr("Modified Date")); ++ } ++ headerColumeName = fileItemModel->headerData(2, Qt::Orientation::Horizontal, Qt::DisplayRole).toString(); ++ EXPECT_EQ(headerColumeName, QObject::tr("File Type")); ++ headerColumeName = fileItemModel->headerData(3, Qt::Orientation::Horizontal, Qt::DisplayRole).toString(); ++ EXPECT_EQ(headerColumeName, QObject::tr("File Size")); ++ ++ fileItemModel->setRootUri("trash:///"); ++ headerColumeName = fileItemModel->headerData(0, Qt::Orientation::Horizontal, Qt::DisplayRole).toString(); ++ EXPECT_EQ(headerColumeName, QObject::tr("File Name")); ++ headerColumeName = fileItemModel->headerData(1, Qt::Orientation::Horizontal, Qt::DisplayRole).toString(); ++ EXPECT_EQ(headerColumeName, QObject::tr("Delete Date")); ++ headerColumeName = fileItemModel->headerData(2, Qt::Orientation::Horizontal, Qt::DisplayRole).toString(); ++ EXPECT_EQ(headerColumeName, QObject::tr("File Type")); ++ headerColumeName = fileItemModel->headerData(3, Qt::Orientation::Horizontal, Qt::DisplayRole).toString(); ++ EXPECT_EQ(headerColumeName, QObject::tr("File Size")); ++ headerColumeName = fileItemModel->headerData(4, Qt::Orientation::Horizontal, Qt::DisplayRole).toString(); ++ EXPECT_EQ(headerColumeName, QObject::tr("Original Path")); ++ ++} ++ ++ ++#include "side-bar-file-system-item.h" ++#include "side-bar-model.h" ++class SideBarModelTest : public testing::Test ++{ ++public: ++ Peony::SideBarModel* sideBarModel = new Peony::SideBarModel; ++private: ++ //打桩声明 ++ stub_ext::StubExt stub; ++}; ++TEST_F(SideBarModelTest, computerFindChildren){ ++ Peony::SideBarFileSystemItem *computerItem = new Peony::SideBarFileSystemItem(nullptr, nullptr, nullptr, sideBarModel); ++ computerItem->findChildren(); ++ auto children = computerItem->m_children; ++ for (auto item : *children) { ++ bool bFileSystem = item->m_uri == "file:///"; ++ if (item->m_uri == "file:///") {/* 侧边栏计算机项的子项存在文件系统 */ ++ ASSERT_TRUE(item->m_mountPoint == "/"); ++ ASSERT_TRUE(item->m_mounted == true); ++ EXPECT_EQ(item->m_displayName, QObject::tr("File System")); ++ return; ++ } ++ } ++} ++ +diff --git a/tests/kt-gtest/kt-libpeony-gtest/kt-operation-gtest/kt-operation-gtest.pro b/tests/kt-gtest/kt-libpeony-gtest/kt-operation-gtest/kt-operation-gtest.pro +new file mode 100644 +index 0000000..08e17fb +--- /dev/null ++++ b/tests/kt-gtest/kt-libpeony-gtest/kt-operation-gtest/kt-operation-gtest.pro +@@ -0,0 +1,7 @@ ++include($$PWD/../kt-libpeony-gtest.pri) ++include($$PWD/../../test-utils.pri) ++TEMPLATE = app ++# 源文件 ++SOURCES += main.cpp \ ++ tst-copy-operation-test.cpp \ ++ tst_operation.cpp \ +diff --git a/tests/kt-gtest/kt-libpeony-gtest/kt-operation-gtest/main.cpp b/tests/kt-gtest/kt-libpeony-gtest/kt-operation-gtest/main.cpp +new file mode 100644 +index 0000000..1cb8576 +--- /dev/null ++++ b/tests/kt-gtest/kt-libpeony-gtest/kt-operation-gtest/main.cpp +@@ -0,0 +1,8 @@ ++#include <gtest/gtest.h> ++#include <QApplication> ++int main(int argc, char *argv[]) ++{ ++ QApplication app(argc, argv); ++ ::testing::InitGoogleTest(&argc, argv); ++ return RUN_ALL_TESTS(); ++} +diff --git a/tests/kt-gtest/kt-libpeony-gtest/kt-operation-gtest/tst-copy-operation-test.cpp b/tests/kt-gtest/kt-libpeony-gtest/kt-operation-gtest/tst-copy-operation-test.cpp +new file mode 100644 +index 0000000..c4314bb +--- /dev/null ++++ b/tests/kt-gtest/kt-libpeony-gtest/kt-operation-gtest/tst-copy-operation-test.cpp +@@ -0,0 +1,22 @@ ++#include <gtest/gtest.h> ++#include <qtest.h> ++#include <QSignalSpy> ++#include "stubext.h" ++#include "file-operation/file-copy-operation.h" ++using namespace testing; ++ ++class copyOperationTest : public testing::Test ++{ ++public: ++ Peony::FileCopyOperation* copyOp = nullptr; ++private: ++ //打桩声明 ++ stub_ext::StubExt stub; ++ QStringList sourceUris; ++ QString destDirUris; ++}; ++ ++TEST_F(copyOperationTest, copyFile) ++{ ++ ++} +diff --git a/tests/kt-gtest/kt-libpeony-gtest/kt-operation-gtest/tst_operation.cpp b/tests/kt-gtest/kt-libpeony-gtest/kt-operation-gtest/tst_operation.cpp +new file mode 100644 +index 0000000..aba6abe +--- /dev/null ++++ b/tests/kt-gtest/kt-libpeony-gtest/kt-operation-gtest/tst_operation.cpp +@@ -0,0 +1,112 @@ ++#include <gtest/gtest.h> ++#include <gtest/gtest.h> ++#include <qtest.h> ++#include <QDebug> ++#include "stubext.h" ++#include "file-operation/create-template-operation.h" ++#include "file-operation/file-copy-operation.h" ++#include "file-operation/file-move-operation.h" ++#include "file-operation/file-rename-operation.h" ++#include "file-operation/file-trash-operation.h" ++#include "file-operation/file-delete-operation.h" ++#include "file-operation/file-link-operation.h" ++#include "file-operation/file-operation-error-dialog.h" ++#include "file-operation/file-operation-error-dialogs.h" ++#include "file-utils.h" ++using namespace testing; ++//文件基础操作通过性测试,用例验证基础功能能否正常运行 ++class operationTest : public testing::Test ++{ ++public: ++ Peony::FileOperationManager *operationManager = Peony::FileOperationManager::getInstance(); ++ Peony::CreateTemplateOperation *createOp = nullptr; ++ Peony::FileCopyOperation* copyOp = nullptr; ++ Peony::FileMoveOperation* moveOp = nullptr; ++ Peony::FileRenameOperation* renameOp = nullptr; ++ Peony::FileTrashOperation* trashOp = nullptr; ++ Peony::FileDeleteOperation* deleteOp = nullptr; ++ Peony::FileLinkOperation* linkOp = nullptr; ++ Peony::FileOperationErrorDialog* dialogTest = nullptr; ++private: ++ //打桩声明 ++ stub_ext::StubExt stub; ++ QString destDirUris = "file:///data/test/"; ++ ++}; ++ ++TEST_F(operationTest, createFile) ++{ ++ createOp = new Peony::CreateTemplateOperation(destDirUris, Peony::CreateTemplateOperation::EmptyFile, "test"); ++ createOp->run(); ++ bool isFileExsit = Peony::FileUtils::isFileExsit(destDirUris + "/NewFile.txt"); ++ ASSERT_TRUE(isFileExsit); ++} ++ ++TEST_F(operationTest, copyFile) ++{ ++ QStringList sourceUris; ++ sourceUris << "file:///data/test/NewFile.txt"; ++ destDirUris = "file:///data"; ++ copyOp = new Peony::FileCopyOperation(sourceUris, destDirUris); ++ copyOp->run(); ++ bool isFileExsit = Peony::FileUtils::isFileExsit(destDirUris + "/NewFile.txt"); ++ ASSERT_TRUE(isFileExsit); ++} ++ ++TEST_F(operationTest, DeleteFile) ++{ ++ QStringList sourceUris; ++ destDirUris = "file:///data/test2"; ++ createOp = new Peony::CreateTemplateOperation(destDirUris, Peony::CreateTemplateOperation::EmptyFile, "test"); ++ createOp->run(); ++ sourceUris << "file:///data/test2/NewFile.txt"; ++ deleteOp = new Peony::FileDeleteOperation(sourceUris); ++ operationManager->startOperation(deleteOp, true); ++ bool isFileExsit = Peony::FileUtils::isFileExsit(sourceUris.first() + "/NewFile.txt"); ++ ASSERT_FALSE(isFileExsit); ++} ++ ++TEST_F(operationTest, RenameFile) ++{ ++ destDirUris = "file:///data/test/NewFile.txt"; ++ renameOp = new Peony::FileRenameOperation(destDirUris,"abc.txt"); ++ renameOp->run(); ++ bool isFileExsit = Peony::FileUtils::isFileExsit(QString("file:///data/test") + "/abc.txt"); ++ ASSERT_TRUE(isFileExsit); ++} ++ ++ ++TEST_F(operationTest, MoveFile) ++{ ++ QStringList sourceUris; ++ sourceUris << "file:///data/test/abc.txt"; ++ destDirUris = "file:///data"; ++ moveOp = new Peony::FileMoveOperation(sourceUris, destDirUris); ++ moveOp->run(); ++ bool isFileExsit = Peony::FileUtils::isFileExsit(destDirUris + "/abc.txt"); ++ ASSERT_TRUE(isFileExsit); ++} ++ ++TEST_F(operationTest, TrashFile) ++{ ++ QStringList sourceUris; ++ sourceUris << "file:///data/abc.txt"; ++ trashOp = new Peony::FileTrashOperation(sourceUris); ++ trashOp->run(); ++ bool isFileExsit = Peony::FileUtils::isFileExsit(destDirUris + "/abc.txt"); ++ ASSERT_FALSE(isFileExsit); ++} ++ ++TEST_F(operationTest, dialogTest) ++{ ++ dialogTest = new Peony::FileOperationErrorDialog(); ++} ++ ++TEST_F(operationTest, linkTest) ++{ ++ QString srcUri = "file:///data/NewFile.txt"; ++ QString desktopUri = "file:///data/test"; ++ linkOp = new Peony::FileLinkOperation(srcUri, desktopUri); ++ linkOp->run(); ++} ++ +diff --git a/tests/kt-gtest/kt-libpeony-gtest/kt-plugin-iface-gtest/kt-plugin-iface-gtest.pro b/tests/kt-gtest/kt-libpeony-gtest/kt-plugin-iface-gtest/kt-plugin-iface-gtest.pro +new file mode 100644 +index 0000000..40ff91f +--- /dev/null ++++ b/tests/kt-gtest/kt-libpeony-gtest/kt-plugin-iface-gtest/kt-plugin-iface-gtest.pro +@@ -0,0 +1,6 @@ ++include($$PWD/../kt-libpeony-gtest.pri) ++include($$PWD/../../test-utils.pri) ++ ++# 源文件 ++SOURCES += main.cpp \ ++ tst_plugin_iface.cpp \ +diff --git a/tests/kt-gtest/kt-libpeony-gtest/kt-plugin-iface-gtest/main.cpp b/tests/kt-gtest/kt-libpeony-gtest/kt-plugin-iface-gtest/main.cpp +new file mode 100644 +index 0000000..1cb8576 +--- /dev/null ++++ b/tests/kt-gtest/kt-libpeony-gtest/kt-plugin-iface-gtest/main.cpp +@@ -0,0 +1,8 @@ ++#include <gtest/gtest.h> ++#include <QApplication> ++int main(int argc, char *argv[]) ++{ ++ QApplication app(argc, argv); ++ ::testing::InitGoogleTest(&argc, argv); ++ return RUN_ALL_TESTS(); ++} +diff --git a/tests/kt-gtest/kt-libpeony-gtest/kt-plugin-iface-gtest/tst_plugin_iface.cpp b/tests/kt-gtest/kt-libpeony-gtest/kt-plugin-iface-gtest/tst_plugin_iface.cpp +new file mode 100644 +index 0000000..4554705 +--- /dev/null ++++ b/tests/kt-gtest/kt-libpeony-gtest/kt-plugin-iface-gtest/tst_plugin_iface.cpp +@@ -0,0 +1,19 @@ ++#include <gtest/gtest.h> ++#include <qtest.h> ++#include "stubext.h" ++ ++using namespace testing; ++ ++class pluginIfaceTest : public testing::Test ++{ ++public: ++ ++private: ++ //打桩声明 ++ stub_ext::StubExt stub; ++ ++}; ++ ++ ++ ++ +diff --git a/tests/kt-gtest/kt-operation-gtest/kt-operation-gtest.pro b/tests/kt-gtest/kt-operation-gtest/kt-operation-gtest.pro +new file mode 100644 +index 0000000..4537e45 +--- /dev/null ++++ b/tests/kt-gtest/kt-operation-gtest/kt-operation-gtest.pro +@@ -0,0 +1,21 @@ ++include($$PWD/../test-utils.pri) ++ ++QT += core testlib gui widgets ++TEMPLATE = app ++CONFIG += c++11 testcase link_pkgconfig no_testcase_installs no_keywords hide_symbols ++ ++# 打桩和项目代码包含目录(需要根据项目工程位置调配) ++LIB_PEONY_PATH = $$PWD/../../../libpeony-qt ++# 包含目录 ++INCLUDEPATH += $$LIB_PEONY_PATH ++HEADERS += $$files($$LIB_PEONY_PATH/file-operation/file-operation-progress-bar-helper.h) \ ++ ++# 源文件 ++SOURCES += main.cpp \ ++ tst-operation-progress-bar-gtest.cpp \ ++ tst_operationtest.cpp \ ++ $$files($$LIB_PEONY_PATH/file-operation/file-operation-progress-bar-helper.cpp) ++ ++#包含的库和包 ++LIBS += -lpeony -lgio-2.0 -lglib-2.0 ++PKGCONFIG += gio-unix-2.0 +diff --git a/tests/kt-gtest/kt-operation-gtest/main.cpp b/tests/kt-gtest/kt-operation-gtest/main.cpp +new file mode 100644 +index 0000000..1cb8576 +--- /dev/null ++++ b/tests/kt-gtest/kt-operation-gtest/main.cpp +@@ -0,0 +1,8 @@ ++#include <gtest/gtest.h> ++#include <QApplication> ++int main(int argc, char *argv[]) ++{ ++ QApplication app(argc, argv); ++ ::testing::InitGoogleTest(&argc, argv); ++ return RUN_ALL_TESTS(); ++} +diff --git a/tests/kt-gtest/kt-operation-gtest/tst-operation-progress-bar-gtest.cpp b/tests/kt-gtest/kt-operation-gtest/tst-operation-progress-bar-gtest.cpp +new file mode 100644 +index 0000000..75f7b9d +--- /dev/null ++++ b/tests/kt-gtest/kt-operation-gtest/tst-operation-progress-bar-gtest.cpp +@@ -0,0 +1,44 @@ ++#include "gio/gio.h" ++#include <gtest/gtest.h> ++#include <qtest.h> ++#include <QStandardPaths> ++#include <QWidget> ++#include "stubext.h" ++#include "file-operation/file-operation.h" ++#include "file-operation/file-operation-manager.h" ++#include "file-operation/file-operation-progress-bar-helper.h" ++using namespace testing; ++ ++class operationProgressBarTest : public testing::Test ++{ ++public: ++ ++private: ++ //打桩声明 ++ stub_ext::StubExt stub; ++ ++}; ++ ++TEST_F(operationProgressBarTest, timeToStringTest) ++{ ++ int time = 3600; ++// progressBar->m_main_progressbar->timeToString(time); ++ QString stringTime = progressBarHelper::timeToString(time); ++ ASSERT_EQ(stringTime.toStdString(), "01hrs00mins00sec"); ++} ++ ++TEST_F(operationProgressBarTest, calculateSpeed) ++{ ++ qint64 size = 300 * 1024 * 1024; ++ double elapsedSeconds = 2.5; ++ double speed = progressBarHelper::calculateSpeed(size, elapsedSeconds); ++ ASSERT_EQ(speed, 120.00); ++} ++ ++TEST_F(operationProgressBarTest, calculateEstimatedTime) ++{ ++ qint64 size = 300 * 1024 * 1024; ++ double speed = 15.00; ++ int time = progressBarHelper::calculateEstimatedTime(size,speed); ++ ASSERT_EQ(time, 20); ++} +diff --git a/tests/kt-gtest/kt-operation-gtest/tst_operationtest.cpp b/tests/kt-gtest/kt-operation-gtest/tst_operationtest.cpp +new file mode 100644 +index 0000000..e4436b5 +--- /dev/null ++++ b/tests/kt-gtest/kt-operation-gtest/tst_operationtest.cpp +@@ -0,0 +1,54 @@ ++#include <gtest/gtest.h> ++#include <qtest.h> ++#include <QSignalSpy> ++#include "stubext.h" ++#include "file-operation/file-operation.h" ++#include "file-operation/file-copy-operation.h" ++using namespace testing; ++ ++class operationTest : public testing::Test ++{ ++public: ++ Peony::FileOperation* op = new Peony::FileOperation(); ++ ++private: ++ //打桩声明 ++ stub_ext::StubExt stub; ++ ++}; ++ ++class copyOperationTest : public testing::Test ++{ ++public: ++ Peony::FileCopyOperation* copyOp = nullptr; ++private: ++ //打桩声明 ++ stub_ext::StubExt stub; ++ QStringList sourceUris; ++ QString destDirUris; ++}; ++ ++ ++//测试类 ++TEST_F(operationTest, syncDestUriTest) ++{ ++ QString destUri = "file:///home/k1"; ++ bool ret = op->syncDestUri(destUri); ++ ASSERT_TRUE(ret); ++} ++ ++TEST_F(copyOperationTest, copyFile) ++{ ++ sourceUris << "file:///home/k1/1w"; ++ destDirUris = "file:///home/k1/test"; ++ copyOp = new Peony::FileCopyOperation(sourceUris, destDirUris); ++ QSignalSpy spyOpFinished(copyOp, &Peony::FileOperation::operationFinished); ++ ASSERT_TRUE(QThreadPool::globalInstance()->tryStart(copyOp)); ++ ASSERT_TRUE(spyOpFinished.isValid()); ++ ASSERT_TRUE(spyOpFinished.isEmpty()); ++ ASSERT_TRUE(spyOpFinished.wait(2000 * 10)); ++ const int startResult = spyOpFinished.count(); ++ ASSERT_EQ(startResult, 1); ++} ++ ++ +diff --git a/tests/kt-gtest/kt-operation-menu-gtest/kt-operation-menu-gtest.pro b/tests/kt-gtest/kt-operation-menu-gtest/kt-operation-menu-gtest.pro +new file mode 100644 +index 0000000..c25a756 +--- /dev/null ++++ b/tests/kt-gtest/kt-operation-menu-gtest/kt-operation-menu-gtest.pro +@@ -0,0 +1,14 @@ ++include($$PWD/../test-utils.pri) ++QT += core testlib gui widgets dbus ++TEMPLATE = app ++CONFIG += c++11 testcase link_pkgconfig no_testcase_installs ++ ++LIB_PEONY_PATH = $$PWD/../../../libpeony-qt ++ ++INCLUDEPATH += $$LIB_PEONY_PATH ++ ++SOURCES += main.cpp \ ++ tst_operation-menu-test.cpp ++ ++LIBS += -lpeony -lgio-2.0 -lglib-2.0 ++PKGCONFIG += gio-unix-2.0 +diff --git a/tests/kt-gtest/kt-operation-menu-gtest/main.cpp b/tests/kt-gtest/kt-operation-menu-gtest/main.cpp +new file mode 100644 +index 0000000..cfbeb33 +--- /dev/null ++++ b/tests/kt-gtest/kt-operation-menu-gtest/main.cpp +@@ -0,0 +1,9 @@ ++#include <gtest/gtest.h> ++#include <QApplication> ++ ++int main(int argc, char *argv[]) ++{ ++ QApplication app(argc, argv); ++ ::testing::InitGoogleTest(&argc, argv); ++ return RUN_ALL_TESTS(); ++} +diff --git a/tests/kt-gtest/kt-operation-menu-gtest/tst_operation-menu-test.cpp b/tests/kt-gtest/kt-operation-menu-gtest/tst_operation-menu-test.cpp +new file mode 100644 +index 0000000..6105681 +--- /dev/null ++++ b/tests/kt-gtest/kt-operation-menu-gtest/tst_operation-menu-test.cpp +@@ -0,0 +1,27 @@ ++#include <gtest/gtest.h> ++#include <gmock/gmock-matchers.h> ++#include "stubext.h" ++#include "global-settings.h" ++ ++using namespace testing; ++ ++class OperationMenu : public testing::Test { ++public: ++ Peony::GlobalSettings* settings = Peony::GlobalSettings::getInstance(); ++ ++private: ++ stub_ext::StubExt stub; ++}; ++ ++//TEST(tst, tst) ++//{ ++// EXPECT_EQ(1, 1); ++// ASSERT_THAT(0, Eq(0)); ++//} ++ ++TEST_F(OperationMenu, setNetwork) ++{ ++ bool flag = false; ++ settings->setValue(SHOW_NETWORK, flag); ++ EXPECT_EQ(settings->getValue(SHOW_NETWORK).toBool(), flag); ++} +diff --git a/tests/kt-gtest/kt-peony-model-gtest/kt-peony-model-gtest.pro b/tests/kt-gtest/kt-peony-model-gtest/kt-peony-model-gtest.pro +new file mode 100644 +index 0000000..6a9873d +--- /dev/null ++++ b/tests/kt-gtest/kt-peony-model-gtest/kt-peony-model-gtest.pro +@@ -0,0 +1,15 @@ ++include($$PWD/../test-utils.pri) ++QT += core testlib gui widgets dbus ++TEMPLATE = app ++CONFIG += c++11 testcase link_pkgconfig no_testcase_installs ++ ++LIB_PEONY_PATH = $$PWD/../../../libpeony-qt ++ ++ ++INCLUDEPATH += $$LIB_PEONY_PATH ++ ++SOURCES += main.cpp \ ++ peony-model-gtest.cpp ++ ++LIBS += -lpeony -lgio-2.0 -lglib-2.0 ++PKGCONFIG += gio-unix-2.0 +diff --git a/tests/kt-gtest/kt-peony-model-gtest/main.cpp b/tests/kt-gtest/kt-peony-model-gtest/main.cpp +new file mode 100644 +index 0000000..1cb8576 +--- /dev/null ++++ b/tests/kt-gtest/kt-peony-model-gtest/main.cpp +@@ -0,0 +1,8 @@ ++#include <gtest/gtest.h> ++#include <QApplication> ++int main(int argc, char *argv[]) ++{ ++ QApplication app(argc, argv); ++ ::testing::InitGoogleTest(&argc, argv); ++ return RUN_ALL_TESTS(); ++} +diff --git a/tests/kt-gtest/kt-peony-model-gtest/peony-model-gtest.cpp b/tests/kt-gtest/kt-peony-model-gtest/peony-model-gtest.cpp +new file mode 100644 +index 0000000..3947c9f +--- /dev/null ++++ b/tests/kt-gtest/kt-peony-model-gtest/peony-model-gtest.cpp +@@ -0,0 +1,44 @@ ++#include <gtest/gtest.h> ++#include <gmock/gmock-matchers.h> ++#include <QDebug> ++#include <QObject> ++#include "stubext.h" ++ ++#include "model/file-item-model.h" ++#include "model/file-item-proxy-filter-sort-model.h" ++ ++using namespace testing; ++ ++class FileItemModelTest : public testing::Test ++{ ++public: ++ Peony::FileItemModel* fileItemModel = new Peony::FileItemModel; ++private: ++ //打桩声明 ++ stub_ext::StubExt stub; ++}; ++ ++//测试集 ++//TEST(tst, tst) ++//{ ++//} ++ ++ ++//测试类 ++TEST_F(FileItemModelTest, columnCount) ++{ ++ /* 该函数用于验证搜索页面列数(有5列,区别其它常规目录) */ ++ fileItemModel->setRootUri("search:///search_uris=file:///home/kylin/test&name_regexp=demo&recursive=1&search_engine=1"); ++ int columnCount = fileItemModel->columnCount(QModelIndex()); ++ EXPECT_EQ(columnCount, 5); ++} ++ ++TEST_F(FileItemModelTest, headerData) ++{ ++ /* 该函数用于验证搜索页面列头是否有文件路径列和其名称 */ ++ fileItemModel->setRootUri("search:///search_uris=file:///home/kylin/test&name_regexp=demo&recursive=1&search_engine=1"); ++ QString headerColumeName = fileItemModel->headerData(4, Qt::Orientation::Horizontal, Qt::DisplayRole).toString(); ++ EXPECT_EQ(headerColumeName, QObject::tr("Path")); ++} ++ ++ +diff --git a/tests/kt-gtest/kt-peony-model-gtest/tst_test.h b/tests/kt-gtest/kt-peony-model-gtest/tst_test.h +new file mode 100644 +index 0000000..bb4c2e1 +--- /dev/null ++++ b/tests/kt-gtest/kt-peony-model-gtest/tst_test.h +@@ -0,0 +1,15 @@ ++#ifndef TST_TEST_H ++#define TST_TEST_H ++ ++#include <gtest/gtest.h> ++#include <gmock/gmock-matchers.h> ++ ++using namespace testing; ++ ++TEST(test, test) ++{ ++ EXPECT_EQ(1, 1); ++ ASSERT_THAT(0, Eq(0)); ++} ++ ++#endif // TST_TEST_H +diff --git a/tests/kt-gtest/kt-src-gtest/kt-src-gtest.pri b/tests/kt-gtest/kt-src-gtest/kt-src-gtest.pri +new file mode 100644 +index 0000000..dbf9eed +--- /dev/null ++++ b/tests/kt-gtest/kt-src-gtest/kt-src-gtest.pri +@@ -0,0 +1,56 @@ ++# 打桩和项目代码包含目录(需要根据项目工程位置调配) ++LIB_PEONY_PATH = $$PWD/../../../src ++# 包含目录 ++INCLUDEPATH += $$LIB_PEONY_PATH ++include($$LIB_PEONY_PATH/../common.pri) ++include($$LIB_PEONY_PATH/../libpeony-qt/libpeony-qt-header.pri) ++include($$LIB_PEONY_PATH/../3rd-parties/SingleApplication/singleapplication.pri) ++include($$LIB_PEONY_PATH/windows/windows.pri) ++include($$LIB_PEONY_PATH/windows/windows-peony.pri) ++include($$LIB_PEONY_PATH/control/control.pri) ++include($$LIB_PEONY_PATH/../plugin-iface/unstable/window-plugin-iface.pri) ++include($$LIB_PEONY_PATH/../plugin-iface/plugin-iface.pri) ++#include($$LIB_PEONY_PATH/src.pri) ++QT += network core widgets gui concurrent xml KWindowSystem dbus x11extras widgets ++ ++CONFIG += c++11 testcase link_pkgconfig no_testcase_installs no_keywords hide_symbols lrelease ++DEFINES += QAPPLICATION_CLASS=QApplication ++ ++contains(DEFINES, KY_SDK_KABASE) { ++ PKGCONFIG += kysdk-kabase ++} ++ ++contains(DEFINES, KY_SDK_DATACOLLECT) { ++ PKGCONFIG += kysdk-datacollect ++} ++ ++#LIBS += -L$$PWD/../libpeony-qt/ -lpeony ++ ++contains(DEFINES, KY_SDK_QT_WIDGETS) { ++ PKGCONFIG += kysdk-qtwidgets ++} ++ ++contains(DEFINES, KY_SDK_WAYLANDHELPER) { ++ PKGCONFIG += kysdk-waylandhelper ++} ++ ++HEADERS += \ ++ $$LIB_PEONY_PATH/peony-application.h \ ++ $$LIB_PEONY_PATH/peony-main-window-style.h \ ++ $$LIB_PEONY_PATH/main-window-factory-plugin-manager.h \ ++ ++SOURCES += \ ++ $$LIB_PEONY_PATH/main-window-factory-plugin-manager.cpp \ ++ $$LIB_PEONY_PATH/peony-application.cpp \ ++ $$LIB_PEONY_PATH/peony-main-window-style.cpp \ ++ ++WINDOW_PLUGIN_INSTALL_DIRS = $$[QT_INSTALL_LIBS]/peony-main-window ++DEFINES += WINDOW_PLUGIN_INSTALL_DIRS='\\"$${WINDOW_PLUGIN_INSTALL_DIRS}\\"' ++ ++DEFINES += QT_DEPRECATED_WARNINGS ++ ++QMAKE_CXXFLAGS += -execution-charset:utf-8 ++ ++#包含的库和包 ++LIBS +=-lgio-2.0 -lglib-2.0 -lX11 -lukui-log4qt -L$$PWD/../../../libpeony-qt/ -lpeony ++PKGCONFIG +=gio-2.0 glib-2.0 gio-unix-2.0 gsettings-qt libcanberra libnotify udisks2 openssl dconf polkit-gobject-1 +diff --git a/tests/kt-gtest/kt-src-gtest/kt-src-gtest.pro b/tests/kt-gtest/kt-src-gtest/kt-src-gtest.pro +new file mode 100644 +index 0000000..1b43ac5 +--- /dev/null ++++ b/tests/kt-gtest/kt-src-gtest/kt-src-gtest.pro +@@ -0,0 +1,14 @@ ++# 打桩和项目代码包含目录(需要根据项目工程位置调配) ++SRC_PATH = $$PWD/../../../src ++INCLUDEPATH += $$SRC_PATH ++# 包含目录 ++include($$PWD/kt-src-gtest.pri) ++include($$PWD/../test-utils.pri) ++ ++# 源文件 ++SOURCES += main.cpp \ ++ tst_uitest.cpp \ ++ ++#包含的库和包 ++#LIBS += -lpeony -lgio-2.0 -lglib-2.0 ++#PKGCONFIG += gio-unix-2.0 +diff --git a/tests/kt-gtest/kt-src-gtest/main.cpp b/tests/kt-gtest/kt-src-gtest/main.cpp +new file mode 100644 +index 0000000..1cb8576 +--- /dev/null ++++ b/tests/kt-gtest/kt-src-gtest/main.cpp +@@ -0,0 +1,8 @@ ++#include <gtest/gtest.h> ++#include <QApplication> ++int main(int argc, char *argv[]) ++{ ++ QApplication app(argc, argv); ++ ::testing::InitGoogleTest(&argc, argv); ++ return RUN_ALL_TESTS(); ++} +diff --git a/tests/kt-gtest/kt-src-gtest/tst_uitest.cpp b/tests/kt-gtest/kt-src-gtest/tst_uitest.cpp +new file mode 100644 +index 0000000..f433ea2 +--- /dev/null ++++ b/tests/kt-gtest/kt-src-gtest/tst_uitest.cpp +@@ -0,0 +1,48 @@ ++#include <gtest/gtest.h> ++ ++//#include <QSignalSpy> ++#include <QStandardPaths> ++#include "stubext.h" ++#include "../src/windows/main-window-factory.h" ++#include "../src/control/navigation-side-bar.h" ++using namespace testing; ++ ++class uiTest : public testing::Test ++{ ++public: ++ MainWindowFactory* factory = new MainWindowFactory; ++ ++private: ++ //打桩声明 ++ stub_ext::StubExt stub; ++ ++}; ++ ++class sideTest : public testing::Test ++{ ++public: ++ NavigationSideBar* side = new NavigationSideBar; ++private: ++ //打桩声明 ++ stub_ext::StubExt stub; ++ QStringList sourceUris; ++ QString destDirUris; ++}; ++ ++TEST_F(uiTest, createWindow) ++{ ++ QString path = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); ++ QWidget *window = factory->createWindow(path); ++ window->show(); ++} ++ ++TEST_F(uiTest, createWindow2) ++{ ++ QStringList uris; ++ uris<<QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); ++ uris<<QStandardPaths::writableLocation(QStandardPaths::HomeLocation); ++ QWidget *window2 = factory->createWindow(uris); ++ window2->show(); ++} ++ ++ +diff --git a/tests/kt-gtest/kt-test-utils/cpp-stub/addr_any.h b/tests/kt-gtest/kt-test-utils/cpp-stub/addr_any.h +new file mode 100755 +index 0000000..a153f34 +--- /dev/null ++++ b/tests/kt-gtest/kt-test-utils/cpp-stub/addr_any.h +@@ -0,0 +1,280 @@ ++#ifndef __ADDR_ANY_H__ ++#define __ADDR_ANY_H__ ++ ++ ++//linux ++#include <regex.h> ++#include <cxxabi.h> ++//c ++#include <cinttypes> ++#include <cstdio> ++#include <cstdlib> ++ ++//c++ ++#include <string> ++#include <map> ++//project ++#include "elfio.hpp" ++ ++ ++ ++class AddrAny ++{ ++public: ++ AddrAny() ++ { ++ m_init = get_exe_pathname(m_fullname); ++ m_baseaddr = 0; ++ } ++ AddrAny(std::string libname) ++ { ++ m_init = get_lib_pathname_and_baseaddr(libname, m_fullname, m_baseaddr); ++ } ++ ++ int get_local_func_addr_symtab(std::string func_name_regex_str, std::map<std::string,void*>& result) ++ { ++ return get_func_addr(SHT_SYMTAB, STB_LOCAL, func_name_regex_str, result); ++ } ++ int get_global_func_addr_symtab(std::string func_name_regex_str, std::map<std::string,void*>& result) ++ { ++ return get_func_addr(SHT_SYMTAB, STB_GLOBAL, func_name_regex_str, result); ++ } ++ int get_weak_func_addr_symtab(std::string func_name_regex_str, std::map<std::string,void*>& result) ++ { ++ return get_func_addr(SHT_SYMTAB, STB_WEAK, func_name_regex_str, result); ++ } ++ ++ int get_global_func_addr_dynsym( std::string func_name_regex_str, std::map<std::string,void*>& result) ++ { ++ return get_func_addr(SHT_DYNSYM, STB_GLOBAL, func_name_regex_str, result); ++ } ++ int get_weak_func_addr_dynsym(std::string func_name_regex_str, std::map<std::string,void*>& result) ++ { ++ return get_func_addr(SHT_DYNSYM, STB_WEAK, func_name_regex_str, result); ++ } ++ ++private: ++ bool demangle(std::string& s, std::string& name) { ++ int status; ++ char* pname = abi::__cxa_demangle(s.c_str(), 0, 0, &status); ++ if (status != 0) ++ { ++ switch(status) ++ { ++ case -1: name = "memory allocation error"; break; ++ case -2: name = "invalid name given"; break; ++ case -3: name = "internal error: __cxa_demangle: invalid argument"; break; ++ default: name = "unknown error occured"; break; ++ } ++ return false; ++ } ++ name = pname; ++ free(pname); ++ return true; ++ } ++ bool get_exe_pathname( std::string& name) ++ { ++ char line[512]; ++ FILE *fp; ++ uintptr_t base_addr; ++ char perm[5]; ++ unsigned long offset; ++ int pathname_pos; ++ char *pathname; ++ size_t pathname_len; ++ int match = 0; ++ ++ if(NULL == (fp = fopen("/proc/self/maps", "r"))) ++ { ++ return false; ++ } ++ ++ while(fgets(line, sizeof(line), fp)) ++ { ++ if(sscanf(line, "%" PRIxPTR "-%*lx %4s %lx %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) continue; ++ ++ if(0 != offset) continue; ++ ++ //get pathname ++ while(isspace(line[pathname_pos]) && pathname_pos < (int)(sizeof(line) - 1)) ++ pathname_pos += 1; ++ if(pathname_pos >= (int)(sizeof(line) - 1)) continue; ++ pathname = line + pathname_pos; ++ pathname_len = strlen(pathname); ++ if(0 == pathname_len) continue; ++ if(pathname[pathname_len - 1] == '\n') ++ { ++ pathname[pathname_len - 1] = '\0'; ++ pathname_len -= 1; ++ } ++ if(0 == pathname_len) continue; ++ if('[' == pathname[0]) continue; ++ ++ name = pathname; ++ match = 1; ++ break; ++ ++ } ++ fclose(fp); ++ ++ if(0 == match) ++ { ++ return false; ++ } ++ else ++ { ++ return true; ++ } ++ ++ } ++ ++ bool get_lib_pathname_and_baseaddr(std::string pathname_regex_str, std::string& name, unsigned long& addr) ++ { ++ char line[512]; ++ FILE *fp; ++ uintptr_t base_addr; ++ char perm[5]; ++ unsigned long offset; ++ int pathname_pos; ++ char *pathname; ++ size_t pathname_len; ++ int match; ++ regex_t pathname_regex; ++ ++ regcomp(&pathname_regex, pathname_regex_str.c_str(), 0); ++ ++ if(NULL == (fp = fopen("/proc/self/maps", "r"))) ++ { ++ return false; ++ } ++ ++ while(fgets(line, sizeof(line), fp)) ++ { ++ if(sscanf(line, "%" PRIxPTR "-%*lx %4s %lx %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) continue; ++ ++ //check permission ++ if(perm[0] != 'r') continue; ++ if(perm[3] != 'p') continue; //do not touch the shared memory ++ ++ //check offset ++ // ++ //We are trying to find ELF header in memory. ++ //It can only be found at the beginning of a mapped memory regions ++ //whose offset is 0. ++ if(0 != offset) continue; ++ ++ //get pathname ++ while(isspace(line[pathname_pos]) && pathname_pos < (int)(sizeof(line) - 1)) ++ pathname_pos += 1; ++ if(pathname_pos >= (int)(sizeof(line) - 1)) continue; ++ pathname = line + pathname_pos; ++ pathname_len = strlen(pathname); ++ if(0 == pathname_len) continue; ++ if(pathname[pathname_len - 1] == '\n') ++ { ++ pathname[pathname_len - 1] = '\0'; ++ pathname_len -= 1; ++ } ++ if(0 == pathname_len) continue; ++ if('[' == pathname[0]) continue; ++ ++ //check pathname ++ //if we need to hook this elf? ++ match = 0; ++ if(0 == regexec(&pathname_regex, pathname, 0, NULL, 0)) ++ { ++ match = 1; ++ name = pathname; ++ addr = (unsigned long)base_addr; ++ break; ++ } ++ if(0 == match) continue; ++ ++ } ++ fclose(fp); ++ if(0 == match) ++ { ++ return false; ++ } ++ else ++ { ++ return true; ++ } ++ ++ } ++ ++ int get_func_addr(unsigned int ttype, unsigned int stype, std::string& func_name_regex_str, std::map<std::string,void*>& result) ++ { ++ // Create an elfio reader ++ ELFIO::elfio reader; ++ int count = 0; ++ regex_t pathname_regex; ++ ++ if(!m_init) ++ { ++ return -1; ++ } ++ ++ regcomp(&pathname_regex, func_name_regex_str.c_str(), 0); ++ // Load ELF data ++ if(!reader.load(m_fullname.c_str())) ++ { ++ return -1; ++ } ++ ++ ELFIO::Elf_Half sec_num = reader.sections.size(); ++ for(int i = 0; i < sec_num; ++i) ++ { ++ ELFIO::section* psec = reader.sections[i]; ++ // Check section type ++ if(psec->get_type() == ttype) ++ { ++ const ELFIO::symbol_section_accessor symbols( reader, psec ); ++ for ( unsigned int j = 0; j < symbols.get_symbols_num(); ++j ) ++ { ++ std::string name; ++ std::string name_mangle; ++ ELFIO::Elf64_Addr value; ++ ELFIO::Elf_Xword size; ++ unsigned char bind; ++ unsigned char type; ++ ELFIO::Elf_Half section_index; ++ unsigned char other; ++ ++ // Read symbol properties ++ symbols.get_symbol( j, name, value, size, bind, type, section_index, other ); ++ if(type == STT_FUNC && bind == stype) ++ { ++ bool ret = demangle(name,name_mangle); ++ if(ret == true) ++ { ++ if (0 == regexec(&pathname_regex, name_mangle.c_str(), 0, NULL, 0)) ++ { ++ result.insert ( std::pair<std::string,void *>(name_mangle,(void*)(value + m_baseaddr))); ++ count++; ++ } ++ } ++ else ++ { ++ if (0 == regexec(&pathname_regex, name.c_str(), 0, NULL, 0)) ++ { ++ result.insert ( std::pair<std::string,void *>(name,(void*)(value + m_baseaddr))); ++ count++; ++ } ++ } ++ } ++ } ++ break; ++ } ++ } ++ ++ return count; ++ } ++private: ++ bool m_init; ++ std::string m_name; ++ std::string m_fullname; ++ unsigned long m_baseaddr; ++ ++}; ++#endif +diff --git a/tests/kt-gtest/kt-test-utils/cpp-stub/addr_pri.h b/tests/kt-gtest/kt-test-utils/cpp-stub/addr_pri.h +new file mode 100755 +index 0000000..9174bb0 +--- /dev/null ++++ b/tests/kt-gtest/kt-test-utils/cpp-stub/addr_pri.h +@@ -0,0 +1,177 @@ ++#ifndef __ADDR_PRI_H__ ++#define __ADDR_PRI_H__ ++ ++ ++#include <utility> ++#include <type_traits> ++ ++ ++ ++//base on C++11 ++ ++/********************************************************** ++ access private function ++**********************************************************/ ++ ++ ++namespace std { ++ template <bool B, class T = void> ++ using enable_if_t = typename enable_if<B, T>::type; ++ template <class T> ++ using remove_reference_t = typename remove_reference<T>::type; ++} // std ++ ++// Unnamed namespace is used to avoid duplicate symbols if the macros are used ++namespace { ++ namespace private_access_detail { ++ ++ // @tparam TagType, used to declare different "get" funciton overloads for ++ // different members/statics ++ template <typename PtrType, PtrType PtrValue, typename TagType> ++ struct private_access { ++ // Normal lookup cannot find in-class defined (inline) friend functions. ++ friend PtrType get(TagType) { return PtrValue; } ++ }; ++ ++ } // namespace private_access_detail ++} // namespace ++ ++// Used macro naming conventions: ++// The "namespace" of this macro library is PRIVATE_ACCESS, i.e. all ++// macro here has this prefix. ++// All implementation macro, which are not meant to be used directly have the ++// PRIVATE_ACCESS_DETAIL prefix. ++// Some macros have the ABCD_IMPL form, which means they contain the ++// implementation details for the specific ABCD macro. ++ ++#define PRIVATE_ACCESS_DETAIL_CONCATENATE_IMPL(x, y) x##y ++#define PRIVATE_ACCESS_DETAIL_CONCATENATE(x, y) \ ++ PRIVATE_ACCESS_DETAIL_CONCATENATE_IMPL(x, y) ++ ++// @param PtrTypeKind E.g if we have "class A", then it can be "A::*" in case of ++// members, or it can be "*" in case of statics. ++#define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, \ ++ PtrTypeKind) \ ++ namespace { \ ++ namespace private_access_detail { \ ++ /* Tag type, used to declare different get funcitons for different \ ++ * members \ ++ */ \ ++ struct Tag {}; \ ++ /* Explicit instantiation */ \ ++ template struct private_access<decltype(&Class::Name), &Class::Name, \ ++ Tag>; \ ++ /* We can build the PtrType only with two aliases */ \ ++ /* E.g. using PtrType = int(int) *; would be illformed */ \ ++ using PRIVATE_ACCESS_DETAIL_CONCATENATE(Alias_, Tag) = Type; \ ++ using PRIVATE_ACCESS_DETAIL_CONCATENATE(PtrType_, Tag) = \ ++ PRIVATE_ACCESS_DETAIL_CONCATENATE(Alias_, Tag) PtrTypeKind; \ ++ /* Declare the friend function, now it is visible in namespace scope. \ ++ * Note, \ ++ * we could declare it inside the Tag type too, in that case ADL would \ ++ * find \ ++ * the declaration. By choosing to declare it here, the Tag type remains \ ++ * a \ ++ * simple tag type, it has no other responsibilities. */ \ ++ PRIVATE_ACCESS_DETAIL_CONCATENATE(PtrType_, Tag) get(Tag); \ ++ } \ ++ } ++ ++#define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_FIELD(Tag, Class, Type, Name) \ ++ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, Class::*) \ ++ namespace { \ ++ namespace access_private_field { \ ++ Type &Class##Name(Class &&t) { return t.*get(private_access_detail::Tag{}); } \ ++ Type &Class##Name(Class &t) { return t.*get(private_access_detail::Tag{}); } \ ++ /* The following usings are here to avoid duplicate const qualifier \ ++ * warnings \ ++ */ \ ++ using PRIVATE_ACCESS_DETAIL_CONCATENATE(X, Tag) = Type; \ ++ using PRIVATE_ACCESS_DETAIL_CONCATENATE(Y, Tag) = \ ++ const PRIVATE_ACCESS_DETAIL_CONCATENATE(X, Tag); \ ++ PRIVATE_ACCESS_DETAIL_CONCATENATE(Y, Tag) & Class##Name(const Class &t) {\ ++ return t.*get(private_access_detail::Tag{}); \ ++ } \ ++ } \ ++ } ++ ++#define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_FUN(Tag, Class, Type, Name) \ ++ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, Class::*) \ ++ namespace { \ ++ namespace call_private_fun { \ ++ /* We do perfect forwarding, but we want to restrict the overload set \ ++ * only for objects which have the type Class. */ \ ++ template <typename Obj, \ ++ std::enable_if_t<std::is_same<std::remove_reference_t<Obj>, \ ++ Class>::value> * = nullptr, \ ++ typename... Args> \ ++ auto Class##Name(Obj &&o, Args &&... args) -> decltype( \ ++ (std::forward<Obj>(o).* \ ++ get(private_access_detail::Tag{}))(std::forward<Args>(args)...)) { \ ++ return (std::forward<Obj>(o).*get(private_access_detail::Tag{}))( \ ++ std::forward<Args>(args)...); \ ++ } \ ++ } \ ++ namespace get_private_fun { \ ++ auto Class##Name() -> decltype( \ ++ get(private_access_detail::Tag{})) { \ ++ return (get(private_access_detail::Tag{})); \ ++ } \ ++ } \ ++ } ++ ++#define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_STATIC_FIELD(Tag, Class, Type, \ ++ Name) \ ++ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, *) \ ++ namespace { \ ++ namespace access_private_static_field { \ ++ namespace Class { \ ++ Type &Class##Name() { return *get(private_access_detail::Tag{}); } \ ++ } \ ++ } \ ++ } ++ ++#define PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_STATIC_FUN(Tag, Class, Type, \ ++ Name) \ ++ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE(Tag, Class, Type, Name, *) \ ++ namespace { \ ++ namespace call_private_static_fun { \ ++ namespace Class { \ ++ template <typename... Args> \ ++ auto Class##Name(Args &&... args) -> decltype( \ ++ get(private_access_detail::Tag{})(std::forward<Args>(args)...)) { \ ++ return get(private_access_detail::Tag{})( \ ++ std::forward<Args>(args)...); \ ++ } \ ++ } \ ++ } \ ++ namespace get_private_static_fun { \ ++ namespace Class { \ ++ auto Class##Name() -> decltype(get(private_access_detail::Tag{})) { \ ++ return get(private_access_detail::Tag{}); \ ++ } \ ++ } \ ++ } \ ++ } ++ ++#define PRIVATE_ACCESS_DETAIL_UNIQUE_TAG \ ++ PRIVATE_ACCESS_DETAIL_CONCATENATE(PrivateAccessTag, __COUNTER__) ++ ++#define ACCESS_PRIVATE_FIELD(Class, Type, Name) \ ++ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_FIELD(PRIVATE_ACCESS_DETAIL_UNIQUE_TAG, \ ++ Class, Type, Name) ++ ++#define ACCESS_PRIVATE_FUN(Class, Type, Name) \ ++ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_FUN(PRIVATE_ACCESS_DETAIL_UNIQUE_TAG, \ ++ Class, Type, Name) ++ ++#define ACCESS_PRIVATE_STATIC_FIELD(Class, Type, Name) \ ++ Type Class::Name; \ ++ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_STATIC_FIELD( \ ++ PRIVATE_ACCESS_DETAIL_UNIQUE_TAG, Class, Type, Name) ++ ++#define ACCESS_PRIVATE_STATIC_FUN(Class, Type, Name) \ ++ PRIVATE_ACCESS_DETAIL_ACCESS_PRIVATE_STATIC_FUN( \ ++ PRIVATE_ACCESS_DETAIL_UNIQUE_TAG, Class, Type, Name) ++ ++#endif +diff --git a/tests/kt-gtest/kt-test-utils/cpp-stub/elfio.hpp b/tests/kt-gtest/kt-test-utils/cpp-stub/elfio.hpp +new file mode 100755 +index 0000000..dd5c9ae +--- /dev/null ++++ b/tests/kt-gtest/kt-test-utils/cpp-stub/elfio.hpp +@@ -0,0 +1,4888 @@ ++ ++/*** Start of inlined file: elfio_dump.hpp ***/ ++#ifndef ELFIO_DUMP_HPP ++#define ELFIO_DUMP_HPP ++ ++#include <algorithm> ++#include <string> ++#include <ostream> ++#include <sstream> ++#include <iomanip> ++ ++/*** Start of inlined file: elfio.hpp ***/ ++#ifndef ELFIO_HPP ++#define ELFIO_HPP ++ ++#ifdef _MSC_VER ++#pragma warning( push ) ++#pragma warning( disable : 4996 ) ++#pragma warning( disable : 4355 ) ++#pragma warning( disable : 4244 ) ++#endif ++ ++#include <string> ++#include <iostream> ++#include <fstream> ++#include <functional> ++#include <algorithm> ++#include <vector> ++#include <deque> ++#include <iterator> ++ ++ ++/*** Start of inlined file: elf_types.hpp ***/ ++#ifndef ELFTYPES_H ++#define ELFTYPES_H ++ ++#ifndef ELFIO_NO_OWN_TYPES ++#if !defined( ELFIO_NO_CSTDINT ) && !defined( ELFIO_NO_INTTYPES ) ++#include <stdint.h> ++#else ++typedef unsigned char uint8_t; ++typedef signed char int8_t; ++typedef unsigned short uint16_t; ++typedef signed short int16_t; ++#ifdef _MSC_VER ++typedef unsigned __int32 uint32_t; ++typedef signed __int32 int32_t; ++typedef unsigned __int64 uint64_t; ++typedef signed __int64 int64_t; ++#else ++typedef unsigned int uint32_t; ++typedef signed int int32_t; ++typedef unsigned long long uint64_t; ++typedef signed long long int64_t; ++#endif // _MSC_VER ++#endif // ELFIO_NO_CSTDINT ++#endif // ELFIO_NO_OWN_TYPES ++ ++namespace ELFIO { ++ ++// Attention! Platform depended definitions. ++typedef uint16_t Elf_Half; ++typedef uint32_t Elf_Word; ++typedef int32_t Elf_Sword; ++typedef uint64_t Elf_Xword; ++typedef int64_t Elf_Sxword; ++ ++typedef uint32_t Elf32_Addr; ++typedef uint32_t Elf32_Off; ++typedef uint64_t Elf64_Addr; ++typedef uint64_t Elf64_Off; ++ ++#define Elf32_Half Elf_Half ++#define Elf64_Half Elf_Half ++#define Elf32_Word Elf_Word ++#define Elf64_Word Elf_Word ++#define Elf32_Sword Elf_Sword ++#define Elf64_Sword Elf_Sword ++ ++/////////////////////// ++// ELF Header Constants ++ ++// File type ++#define ET_NONE 0 ++#define ET_REL 1 ++#define ET_EXEC 2 ++#define ET_DYN 3 ++#define ET_CORE 4 ++#define ET_LOOS 0xFE00 ++#define ET_HIOS 0xFEFF ++#define ET_LOPROC 0xFF00 ++#define ET_HIPROC 0xFFFF ++ ++#define EM_NONE 0 // No machine ++#define EM_M32 1 // AT&T WE 32100 ++#define EM_SPARC 2 // SUN SPARC ++#define EM_386 3 // Intel 80386 ++#define EM_68K 4 // Motorola m68k family ++#define EM_88K 5 // Motorola m88k family ++#define EM_486 6 // Intel 80486// Reserved for future use ++#define EM_860 7 // Intel 80860 ++#define EM_MIPS 8 // MIPS R3000 (officially, big-endian only) ++#define EM_S370 9 // IBM System/370 ++#define EM_MIPS_RS3_LE \ ++ 10 // MIPS R3000 little-endian (Oct 4 1999 Draft) Deprecated ++#define EM_res011 11 // Reserved ++#define EM_res012 12 // Reserved ++#define EM_res013 13 // Reserved ++#define EM_res014 14 // Reserved ++#define EM_PARISC 15 // HPPA ++#define EM_res016 16 // Reserved ++#define EM_VPP550 17 // Fujitsu VPP500 ++#define EM_SPARC32PLUS 18 // Sun's "v8plus" ++#define EM_960 19 // Intel 80960 ++#define EM_PPC 20 // PowerPC ++#define EM_PPC64 21 // 64-bit PowerPC ++#define EM_S390 22 // IBM S/390 ++#define EM_SPU 23 // Sony/Toshiba/IBM SPU ++#define EM_res024 24 // Reserved ++#define EM_res025 25 // Reserved ++#define EM_res026 26 // Reserved ++#define EM_res027 27 // Reserved ++#define EM_res028 28 // Reserved ++#define EM_res029 29 // Reserved ++#define EM_res030 30 // Reserved ++#define EM_res031 31 // Reserved ++#define EM_res032 32 // Reserved ++#define EM_res033 33 // Reserved ++#define EM_res034 34 // Reserved ++#define EM_res035 35 // Reserved ++#define EM_V800 36 // NEC V800 series ++#define EM_FR20 37 // Fujitsu FR20 ++#define EM_RH32 38 // TRW RH32 ++#define EM_MCORE 39 // Motorola M*Core // May also be taken by Fujitsu MMA ++#define EM_RCE 39 // Old name for MCore ++#define EM_ARM 40 // ARM ++#define EM_OLD_ALPHA 41 // Digital Alpha ++#define EM_SH 42 // Renesas (formerly Hitachi) / SuperH SH ++#define EM_SPARCV9 43 // SPARC v9 64-bit ++#define EM_TRICORE 44 // Siemens Tricore embedded processor ++#define EM_ARC 45 // ARC Cores ++#define EM_H8_300 46 // Renesas (formerly Hitachi) H8/300 ++#define EM_H8_300H 47 // Renesas (formerly Hitachi) H8/300H ++#define EM_H8S 48 // Renesas (formerly Hitachi) H8S ++#define EM_H8_500 49 // Renesas (formerly Hitachi) H8/500 ++#define EM_IA_64 50 // Intel IA-64 Processor ++#define EM_MIPS_X 51 // Stanford MIPS-X ++#define EM_COLDFIRE 52 // Motorola Coldfire ++#define EM_68HC12 53 // Motorola M68HC12 ++#define EM_MMA 54 // Fujitsu Multimedia Accelerator ++#define EM_PCP 55 // Siemens PCP ++#define EM_NCPU 56 // Sony nCPU embedded RISC processor ++#define EM_NDR1 57 // Denso NDR1 microprocesspr ++#define EM_STARCORE 58 // Motorola Star*Core processor ++#define EM_ME16 59 // Toyota ME16 processor ++#define EM_ST100 60 // STMicroelectronics ST100 processor ++#define EM_TINYJ 61 // Advanced Logic Corp. TinyJ embedded processor ++#define EM_X86_64 62 // Advanced Micro Devices X86-64 processor ++#define EM_PDSP 63 // Sony DSP Processor ++#define EM_PDP10 64 // Digital Equipment Corp. PDP-10 ++#define EM_PDP11 65 // Digital Equipment Corp. PDP-11 ++#define EM_FX66 66 // Siemens FX66 microcontroller ++#define EM_ST9PLUS 67 // STMicroelectronics ST9+ 8/16 bit microcontroller ++#define EM_ST7 68 // STMicroelectronics ST7 8-bit microcontroller ++#define EM_68HC16 69 // Motorola MC68HC16 Microcontroller ++#define EM_68HC11 70 // Motorola MC68HC11 Microcontroller ++#define EM_68HC08 71 // Motorola MC68HC08 Microcontroller ++#define EM_68HC05 72 // Motorola MC68HC05 Microcontroller ++#define EM_SVX 73 // Silicon Graphics SVx ++#define EM_ST19 74 // STMicroelectronics ST19 8-bit cpu ++#define EM_VAX 75 // Digital VAX ++#define EM_CRIS 76 // Axis Communications 32-bit embedded processor ++#define EM_JAVELIN 77 // Infineon Technologies 32-bit embedded cpu ++#define EM_FIREPATH 78 // Element 14 64-bit DSP processor ++#define EM_ZSP 79 // LSI Logic's 16-bit DSP processor ++#define EM_MMIX 80 // Donald Knuth's educational 64-bit processor ++#define EM_HUANY 81 // Harvard's machine-independent format ++#define EM_PRISM 82 // SiTera Prism ++#define EM_AVR 83 // Atmel AVR 8-bit microcontroller ++#define EM_FR30 84 // Fujitsu FR30 ++#define EM_D10V 85 // Mitsubishi D10V ++#define EM_D30V 86 // Mitsubishi D30V ++#define EM_V850 87 // NEC v850 ++#define EM_M32R 88 // Renesas M32R (formerly Mitsubishi M32R) ++#define EM_MN10300 89 // Matsushita MN10300 ++#define EM_MN10200 90 // Matsushita MN10200 ++#define EM_PJ 91 // picoJava ++#define EM_OPENRISC 92 // OpenRISC 32-bit embedded processor ++#define EM_ARC_A5 93 // ARC Cores Tangent-A5 ++#define EM_XTENSA 94 // Tensilica Xtensa Architecture ++#define EM_VIDEOCORE 95 // Alphamosaic VideoCore processor ++#define EM_TMM_GPP 96 // Thompson Multimedia General Purpose Processor ++#define EM_NS32K 97 // National Semiconductor 32000 series ++#define EM_TPC 98 // Tenor Network TPC processor ++#define EM_SNP1K 99 // Trebia SNP 1000 processor ++#define EM_ST200 100 // STMicroelectronics ST200 microcontroller ++#define EM_IP2K 101 // Ubicom IP2022 micro controller ++#define EM_MAX 102 // MAX Processor ++#define EM_CR 103 // National Semiconductor CompactRISC ++#define EM_F2MC16 104 // Fujitsu F2MC16 ++#define EM_MSP430 105 // TI msp430 micro controller ++#define EM_BLACKFIN 106 // ADI Blackfin ++#define EM_SE_C33 107 // S1C33 Family of Seiko Epson processors ++#define EM_SEP 108 // Sharp embedded microprocessor ++#define EM_ARCA 109 // Arca RISC Microprocessor ++#define EM_UNICORE \ ++ 110 // Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University ++#define EM_EXCESS 111 // eXcess: 16/32/64-bit configurable embedded CPU ++#define EM_DXP 112 // Icera Semiconductor Inc. Deep Execution Processor ++#define EM_ALTERA_NIOS2 113 // Altera Nios II soft-core processor ++#define EM_CRX 114 // National Semiconductor CRX ++#define EM_XGATE 115 // Motorola XGATE embedded processor ++#define EM_C166 116 // Infineon C16x/XC16x processor ++#define EM_M16C 117 // Renesas M16C series microprocessors ++#define EM_DSPIC30F \ ++ 118 // Microchip Technology dsPIC30F Digital Signal Controller ++#define EM_CE 119 // Freescale Communication Engine RISC core ++#define EM_M32C 120 // Renesas M32C series microprocessors ++#define EM_res121 121 // Reserved ++#define EM_res122 122 // Reserved ++#define EM_res123 123 // Reserved ++#define EM_res124 124 // Reserved ++#define EM_res125 125 // Reserved ++#define EM_res126 126 // Reserved ++#define EM_res127 127 // Reserved ++#define EM_res128 128 // Reserved ++#define EM_res129 129 // Reserved ++#define EM_res130 130 // Reserved ++#define EM_TSK3000 131 // Altium TSK3000 core ++#define EM_RS08 132 // Freescale RS08 embedded processor ++#define EM_res133 133 // Reserved ++#define EM_ECOG2 134 // Cyan Technology eCOG2 microprocessor ++#define EM_SCORE 135 // Sunplus Score ++#define EM_SCORE7 135 // Sunplus S+core7 RISC processor ++#define EM_DSP24 136 // New Japan Radio (NJR) 24-bit DSP Processor ++#define EM_VIDEOCORE3 137 // Broadcom VideoCore III processor ++#define EM_LATTICEMICO32 138 // RISC processor for Lattice FPGA architecture ++#define EM_SE_C17 139 // Seiko Epson C17 family ++#define EM_TI_C6000 140 // Texas Instruments TMS320C6000 DSP family ++#define EM_TI_C2000 141 // Texas Instruments TMS320C2000 DSP family ++#define EM_TI_C5500 142 // Texas Instruments TMS320C55x DSP family ++#define EM_res143 143 // Reserved ++#define EM_res144 144 // Reserved ++#define EM_res145 145 // Reserved ++#define EM_res146 146 // Reserved ++#define EM_res147 147 // Reserved ++#define EM_res148 148 // Reserved ++#define EM_res149 149 // Reserved ++#define EM_res150 150 // Reserved ++#define EM_res151 151 // Reserved ++#define EM_res152 152 // Reserved ++#define EM_res153 153 // Reserved ++#define EM_res154 154 // Reserved ++#define EM_res155 155 // Reserved ++#define EM_res156 156 // Reserved ++#define EM_res157 157 // Reserved ++#define EM_res158 158 // Reserved ++#define EM_res159 159 // Reserved ++#define EM_MMDSP_PLUS 160 // STMicroelectronics 64bit VLIW Data Signal Processor ++#define EM_CYPRESS_M8C 161 // Cypress M8C microprocessor ++#define EM_R32C 162 // Renesas R32C series microprocessors ++#define EM_TRIMEDIA 163 // NXP Semiconductors TriMedia architecture family ++#define EM_QDSP6 164 // QUALCOMM DSP6 Processor ++#define EM_8051 165 // Intel 8051 and variants ++#define EM_STXP7X 166 // STMicroelectronics STxP7x family ++#define EM_NDS32 \ ++ 167 // Andes Technology compact code size embedded RISC processor family ++#define EM_ECOG1 168 // Cyan Technology eCOG1X family ++#define EM_ECOG1X 168 // Cyan Technology eCOG1X family ++#define EM_MAXQ30 169 // Dallas Semiconductor MAXQ30 Core Micro-controllers ++#define EM_XIMO16 170 // New Japan Radio (NJR) 16-bit DSP Processor ++#define EM_MANIK 171 // M2000 Reconfigurable RISC Microprocessor ++#define EM_CRAYNV2 172 // Cray Inc. NV2 vector architecture ++#define EM_RX 173 // Renesas RX family ++#define EM_METAG 174 // Imagination Technologies META processor architecture ++#define EM_MCST_ELBRUS 175 // MCST Elbrus general purpose hardware architecture ++#define EM_ECOG16 176 // Cyan Technology eCOG16 family ++#define EM_CR16 177 // National Semiconductor CompactRISC 16-bit processor ++#define EM_ETPU 178 // Freescale Extended Time Processing Unit ++#define EM_SLE9X 179 // Infineon Technologies SLE9X core ++#define EM_L1OM 180 // Intel L1OM ++#define EM_INTEL181 181 // Reserved by Intel ++#define EM_INTEL182 182 // Reserved by Intel ++#define EM_res183 183 // Reserved by ARM ++#define EM_res184 184 // Reserved by ARM ++#define EM_AVR32 185 // Atmel Corporation 32-bit microprocessor family ++#define EM_STM8 186 // STMicroeletronics STM8 8-bit microcontroller ++#define EM_TILE64 187 // Tilera TILE64 multicore architecture family ++#define EM_TILEPRO 188 // Tilera TILEPro multicore architecture family ++#define EM_MICROBLAZE 189 // Xilinx MicroBlaze 32-bit RISC soft processor core ++#define EM_CUDA 190 // NVIDIA CUDA architecture ++#define EM_TILEGX 191 // Tilera TILE-Gx multicore architecture family ++#define EM_CLOUDSHIELD 192 // CloudShield architecture family ++#define EM_COREA_1ST 193 // KIPO-KAIST Core-A 1st generation processor family ++#define EM_COREA_2ND 194 // KIPO-KAIST Core-A 2nd generation processor family ++#define EM_ARC_COMPACT2 195 // Synopsys ARCompact V2 ++#define EM_OPEN8 196 // Open8 8-bit RISC soft processor core ++#define EM_RL78 197 // Renesas RL78 family ++#define EM_VIDEOCORE5 198 // Broadcom VideoCore V processor ++#define EM_78KOR 199 // Renesas 78KOR family ++#define EM_56800EX 200 // Freescale 56800EX Digital Signal Controller (DSC) ++#define EM_BA1 201 // Beyond BA1 CPU architecture ++#define EM_BA2 202 // Beyond BA2 CPU architecture ++#define EM_XCORE 203 // XMOS xCORE processor family ++#define EM_MCHP_PIC 204 // Microchip 8-bit PIC(r) family ++#define EM_INTEL205 205 // Reserved by Intel ++#define EM_INTEL206 206 // Reserved by Intel ++#define EM_INTEL207 207 // Reserved by Intel ++#define EM_INTEL208 208 // Reserved by Intel ++#define EM_INTEL209 209 // Reserved by Intel ++#define EM_KM32 210 // KM211 KM32 32-bit processor ++#define EM_KMX32 211 // KM211 KMX32 32-bit processor ++#define EM_KMX16 212 // KM211 KMX16 16-bit processor ++#define EM_KMX8 213 // KM211 KMX8 8-bit processor ++#define EM_KVARC 214 // KM211 KVARC processor ++#define EM_CDP 215 // Paneve CDP architecture family ++#define EM_COGE 216 // Cognitive Smart Memory Processor ++#define EM_COOL 217 // iCelero CoolEngine ++#define EM_NORC 218 // Nanoradio Optimized RISC ++#define EM_CSR_KALIMBA 219 // CSR Kalimba architecture family ++#define EM_Z80 220 // Zilog Z80 ++#define EM_VISIUM 221 // Controls and Data Services VISIUMcore processor ++#define EM_FT32 222 // FTDI Chip FT32 high performance 32-bit RISC architecture ++#define EM_MOXIE 223 // Moxie processor family ++#define EM_AMDGPU 224 // AMD GPU architecture ++#define EM_RISCV 243 // RISC-V ++#define EM_LANAI 244 // Lanai processor ++#define EM_CEVA 245 // CEVA Processor Architecture Family ++#define EM_CEVA_X2 246 // CEVA X2 Processor Family ++#define EM_BPF 247 // Linux BPF – in-kernel virtual machine ++#define EM_GRAPHCORE_IPU 248 // Graphcore Intelligent Processing Unit ++#define EM_IMG1 249 // Imagination Technologies ++#define EM_NFP 250 // Netronome Flow Processor (P) ++#define EM_CSKY 252 // C-SKY processor family ++ ++// File version ++#define EV_NONE 0 ++#define EV_CURRENT 1 ++ ++// Identification index ++#define EI_MAG0 0 ++#define EI_MAG1 1 ++#define EI_MAG2 2 ++#define EI_MAG3 3 ++#define EI_CLASS 4 ++#define EI_DATA 5 ++#define EI_VERSION 6 ++#define EI_OSABI 7 ++#define EI_ABIVERSION 8 ++#define EI_PAD 9 ++#define EI_NIDENT 16 ++ ++// Magic number ++#define ELFMAG0 0x7F ++#define ELFMAG1 'E' ++#define ELFMAG2 'L' ++#define ELFMAG3 'F' ++ ++// File class ++#define ELFCLASSNONE 0 ++#define ELFCLASS32 1 ++#define ELFCLASS64 2 ++ ++// Encoding ++#define ELFDATANONE 0 ++#define ELFDATA2LSB 1 ++#define ELFDATA2MSB 2 ++ ++// OS extensions ++#define ELFOSABI_NONE 0 // No extensions or unspecified ++#define ELFOSABI_HPUX 1 // Hewlett-Packard HP-UX ++#define ELFOSABI_NETBSD 2 // NetBSD ++#define ELFOSABI_LINUX 3 // Linux ++#define ELFOSABI_SOLARIS 6 // Sun Solaris ++#define ELFOSABI_AIX 7 // AIX ++#define ELFOSABI_IRIX 8 // IRIX ++#define ELFOSABI_FREEBSD 9 // FreeBSD ++#define ELFOSABI_TRU64 10 // Compaq TRU64 UNIX ++#define ELFOSABI_MODESTO 11 // Novell Modesto ++#define ELFOSABI_OPENBSD 12 // Open BSD ++#define ELFOSABI_OPENVMS 13 // Open VMS ++#define ELFOSABI_NSK 14 // Hewlett-Packard Non-Stop Kernel ++#define ELFOSABI_AROS 15 // Amiga Research OS ++#define ELFOSABI_FENIXOS 16 // The FenixOS highly scalable multi-core OS ++// 64-255 Architecture-specific value range ++#define ELFOSABI_AMDGPU_HSA \ ++ 64 // AMDGPU OS for HSA compatible compute // kernels. ++#define ELFOSABI_AMDGPU_PAL \ ++ 65 // AMDGPU OS for AMD PAL compatible graphics // shaders and compute kernels. ++#define ELFOSABI_AMDGPU_MESA3D \ ++ 66 // AMDGPU OS for Mesa3D compatible graphics // shaders and compute kernels. ++ ++// AMDGPU specific e_flags ++#define EF_AMDGPU_MACH 0x0ff // AMDGPU processor selection mask. ++#define EF_AMDGPU_XNACK \ ++ 0x100 // Indicates if the XNACK target feature is // enabled for all code contained in the ELF. ++// AMDGPU processors ++#define EF_AMDGPU_MACH_NONE 0x000 // Unspecified processor. ++#define EF_AMDGPU_MACH_R600_R600 0x001 ++#define EF_AMDGPU_MACH_R600_R630 0x002 ++#define EF_AMDGPU_MACH_R600_RS880 0x003 ++#define EF_AMDGPU_MACH_R600_RV670 0x004 ++#define EF_AMDGPU_MACH_R600_RV710 0x005 ++#define EF_AMDGPU_MACH_R600_RV730 0x006 ++#define EF_AMDGPU_MACH_R600_RV770 0x007 ++#define EF_AMDGPU_MACH_R600_CEDAR 0x008 ++#define EF_AMDGPU_MACH_R600_CYPRESS 0x009 ++#define EF_AMDGPU_MACH_R600_JUNIPER 0x00a ++#define EF_AMDGPU_MACH_R600_REDWOOD 0x00b ++#define EF_AMDGPU_MACH_R600_SUMO 0x00c ++#define EF_AMDGPU_MACH_R600_BARTS 0x00d ++#define EF_AMDGPU_MACH_R600_CAICOS 0x00e ++#define EF_AMDGPU_MACH_R600_CAYMAN 0x00f ++#define EF_AMDGPU_MACH_R600_TURKS 0x010 ++#define EF_AMDGPU_MACH_R600_RESERVED_FIRST 0x011 ++#define EF_AMDGPU_MACH_R600_RESERVED_LAST 0x01f ++#define EF_AMDGPU_MACH_R600_FIRST EF_AMDGPU_MACH_R600_R600 ++#define EF_AMDGPU_MACH_R600_LAST EF_AMDGPU_MACH_R600_TURKS ++#define EF_AMDGPU_MACH_AMDGCN_GFX600 0x020 ++#define EF_AMDGPU_MACH_AMDGCN_GFX601 0x021 ++#define EF_AMDGPU_MACH_AMDGCN_GFX700 0x022 ++#define EF_AMDGPU_MACH_AMDGCN_GFX701 0x023 ++#define EF_AMDGPU_MACH_AMDGCN_GFX702 0x024 ++#define EF_AMDGPU_MACH_AMDGCN_GFX703 0x025 ++#define EF_AMDGPU_MACH_AMDGCN_GFX704 0x026 ++#define EF_AMDGPU_MACH_AMDGCN_GFX801 0x028 ++#define EF_AMDGPU_MACH_AMDGCN_GFX802 0x029 ++#define EF_AMDGPU_MACH_AMDGCN_GFX803 0x02a ++#define EF_AMDGPU_MACH_AMDGCN_GFX810 0x02b ++#define EF_AMDGPU_MACH_AMDGCN_GFX900 0x02c ++#define EF_AMDGPU_MACH_AMDGCN_GFX902 0x02d ++#define EF_AMDGPU_MACH_AMDGCN_GFX904 0x02e ++#define EF_AMDGPU_MACH_AMDGCN_GFX906 0x02f ++#define EF_AMDGPU_MACH_AMDGCN_RESERVED0 0x027 ++#define EF_AMDGPU_MACH_AMDGCN_RESERVED1 0x030 ++#define EF_AMDGPU_MACH_AMDGCN_FIRST EF_AMDGPU_MACH_AMDGCN_GFX600 ++#define EF_AMDGPU_MACH_AMDGCN_LAST EF_AMDGPU_MACH_AMDGCN_GFX906 ++ ++///////////////////// ++// Sections constants ++ ++// Section indexes ++#define SHN_UNDEF 0 ++#define SHN_LORESERVE 0xFF00 ++#define SHN_LOPROC 0xFF00 ++#define SHN_HIPROC 0xFF1F ++#define SHN_LOOS 0xFF20 ++#define SHN_HIOS 0xFF3F ++#define SHN_ABS 0xFFF1 ++#define SHN_COMMON 0xFFF2 ++#define SHN_XINDEX 0xFFFF ++#define SHN_HIRESERVE 0xFFFF ++ ++// Section types ++#define SHT_NULL 0 ++#define SHT_PROGBITS 1 ++#define SHT_SYMTAB 2 ++#define SHT_STRTAB 3 ++#define SHT_RELA 4 ++#define SHT_HASH 5 ++#define SHT_DYNAMIC 6 ++#define SHT_NOTE 7 ++#define SHT_NOBITS 8 ++#define SHT_REL 9 ++#define SHT_SHLIB 10 ++#define SHT_DYNSYM 11 ++#define SHT_INIT_ARRAY 14 ++#define SHT_FINI_ARRAY 15 ++#define SHT_PREINIT_ARRAY 16 ++#define SHT_GROUP 17 ++#define SHT_SYMTAB_SHNDX 18 ++#define SHT_LOOS 0x60000000 ++#define SHT_HIOS 0x6fffffff ++#define SHT_LOPROC 0x70000000 ++#define SHT_HIPROC 0x7FFFFFFF ++#define SHT_LOUSER 0x80000000 ++#define SHT_HIUSER 0xFFFFFFFF ++ ++// Section attribute flags ++#define SHF_WRITE 0x1 ++#define SHF_ALLOC 0x2 ++#define SHF_EXECINSTR 0x4 ++#define SHF_MERGE 0x10 ++#define SHF_STRINGS 0x20 ++#define SHF_INFO_LINK 0x40 ++#define SHF_LINK_ORDER 0x80 ++#define SHF_OS_NONCONFORMING 0x100 ++#define SHF_GROUP 0x200 ++#define SHF_TLS 0x400 ++#define SHF_MASKOS 0x0ff00000 ++#define SHF_MASKPROC 0xF0000000 ++ ++// Section group flags ++#define GRP_COMDAT 0x1 ++#define GRP_MASKOS 0x0ff00000 ++#define GRP_MASKPROC 0xf0000000 ++ ++// Symbol binding ++#define STB_LOCAL 0 ++#define STB_GLOBAL 1 ++#define STB_WEAK 2 ++#define STB_LOOS 10 ++#define STB_HIOS 12 ++#define STB_MULTIDEF 13 ++#define STB_LOPROC 13 ++#define STB_HIPROC 15 ++ ++// Note types ++#define NT_AMDGPU_METADATA 1 ++#define NT_AMD_AMDGPU_HSA_METADATA 10 ++#define NT_AMD_AMDGPU_ISA 11 ++#define NT_AMD_AMDGPU_PAL_METADATA 12 ++ ++// Symbol types ++#define STT_NOTYPE 0 ++#define STT_OBJECT 1 ++#define STT_FUNC 2 ++#define STT_SECTION 3 ++#define STT_FILE 4 ++#define STT_COMMON 5 ++#define STT_TLS 6 ++#define STT_LOOS 10 ++#define STT_AMDGPU_HSA_KERNEL 10 ++#define STT_HIOS 12 ++#define STT_LOPROC 13 ++#define STT_HIPROC 15 ++ ++// Symbol visibility ++#define STV_DEFAULT 0 ++#define STV_INTERNAL 1 ++#define STV_HIDDEN 2 ++#define STV_PROTECTED 3 ++ ++// Undefined name ++#define STN_UNDEF 0 ++ ++// Relocation types ++#define R_386_NONE 0 ++#define R_X86_64_NONE 0 ++#define R_AMDGPU_NONE 0 ++#define R_386_32 1 ++#define R_X86_64_64 1 ++#define R_AMDGPU_ABS32_LO 1 ++#define R_386_PC32 2 ++#define R_X86_64_PC32 2 ++#define R_AMDGPU_ABS32_HI 2 ++#define R_386_GOT32 3 ++#define R_X86_64_GOT32 3 ++#define R_AMDGPU_ABS64 3 ++#define R_386_PLT32 4 ++#define R_X86_64_PLT32 4 ++#define R_AMDGPU_REL32 4 ++#define R_386_COPY 5 ++#define R_X86_64_COPY 5 ++#define R_AMDGPU_REL64 5 ++#define R_386_GLOB_DAT 6 ++#define R_X86_64_GLOB_DAT 6 ++#define R_AMDGPU_ABS32 6 ++#define R_386_JMP_SLOT 7 ++#define R_X86_64_JUMP_SLOT 7 ++#define R_AMDGPU_GOTPCREL 7 ++#define R_386_RELATIVE 8 ++#define R_X86_64_RELATIVE 8 ++#define R_AMDGPU_GOTPCREL32_LO 8 ++#define R_386_GOTOFF 9 ++#define R_X86_64_GOTPCREL 9 ++#define R_AMDGPU_GOTPCREL32_HI 9 ++#define R_386_GOTPC 10 ++#define R_X86_64_32 10 ++#define R_AMDGPU_REL32_LO 10 ++#define R_386_32PLT 11 ++#define R_X86_64_32S 11 ++#define R_AMDGPU_REL32_HI 11 ++#define R_X86_64_16 12 ++#define R_X86_64_PC16 13 ++#define R_AMDGPU_RELATIVE64 13 ++#define R_386_TLS_TPOFF 14 ++#define R_X86_64_8 14 ++#define R_386_TLS_IE 15 ++#define R_X86_64_PC8 15 ++#define R_386_TLS_GOTIE 16 ++#define R_X86_64_DTPMOD64 16 ++#define R_386_TLS_LE 17 ++#define R_X86_64_DTPOFF64 17 ++#define R_386_TLS_GD 18 ++#define R_X86_64_TPOFF64 18 ++#define R_386_TLS_LDM 19 ++#define R_X86_64_TLSGD 19 ++#define R_386_16 20 ++#define R_X86_64_TLSLD 20 ++#define R_386_PC16 21 ++#define R_X86_64_DTPOFF32 21 ++#define R_386_8 22 ++#define R_X86_64_GOTTPOFF 22 ++#define R_386_PC8 23 ++#define R_X86_64_TPOFF32 23 ++#define R_386_TLS_GD_32 24 ++#define R_X86_64_PC64 24 ++#define R_386_TLS_GD_PUSH 25 ++#define R_X86_64_GOTOFF64 25 ++#define R_386_TLS_GD_CALL 26 ++#define R_X86_64_GOTPC32 26 ++#define R_386_TLS_GD_POP 27 ++#define R_X86_64_GOT64 27 ++#define R_386_TLS_LDM_32 28 ++#define R_X86_64_GOTPCREL64 28 ++#define R_386_TLS_LDM_PUSH 29 ++#define R_X86_64_GOTPC64 29 ++#define R_386_TLS_LDM_CALL 30 ++#define R_X86_64_GOTPLT64 30 ++#define R_386_TLS_LDM_POP 31 ++#define R_X86_64_PLTOFF64 31 ++#define R_386_TLS_LDO_32 32 ++#define R_386_TLS_IE_32 33 ++#define R_386_TLS_LE_32 34 ++#define R_X86_64_GOTPC32_TLSDESC 34 ++#define R_386_TLS_DTPMOD32 35 ++#define R_X86_64_TLSDESC_CALL 35 ++#define R_386_TLS_DTPOFF32 36 ++#define R_X86_64_TLSDESC 36 ++#define R_386_TLS_TPOFF32 37 ++#define R_X86_64_IRELATIVE 37 ++#define R_386_SIZE32 38 ++#define R_386_TLS_GOTDESC 39 ++#define R_386_TLS_DESC_CALL 40 ++#define R_386_TLS_DESC 41 ++#define R_386_IRELATIVE 42 ++#define R_386_GOT32X 43 ++#define R_X86_64_GNU_VTINHERIT 250 ++#define R_X86_64_GNU_VTENTRY 251 ++ ++// Segment types ++#define PT_NULL 0 ++#define PT_LOAD 1 ++#define PT_DYNAMIC 2 ++#define PT_INTERP 3 ++#define PT_NOTE 4 ++#define PT_SHLIB 5 ++#define PT_PHDR 6 ++#define PT_TLS 7 ++#define PT_LOOS 0x60000000 ++#define PT_HIOS 0x6fffffff ++#define PT_LOPROC 0x70000000 ++#define PT_HIPROC 0x7FFFFFFF ++ ++// Segment flags ++#define PF_X 1 // Execute ++#define PF_W 2 // Write ++#define PF_R 4 // Read ++#define PF_MASKOS 0x0ff00000 // Unspecified ++#define PF_MASKPROC 0xf0000000 // Unspecified ++ ++// Dynamic Array Tags ++#define DT_NULL 0 ++#define DT_NEEDED 1 ++#define DT_PLTRELSZ 2 ++#define DT_PLTGOT 3 ++#define DT_HASH 4 ++#define DT_STRTAB 5 ++#define DT_SYMTAB 6 ++#define DT_RELA 7 ++#define DT_RELASZ 8 ++#define DT_RELAENT 9 ++#define DT_STRSZ 10 ++#define DT_SYMENT 11 ++#define DT_INIT 12 ++#define DT_FINI 13 ++#define DT_SONAME 14 ++#define DT_RPATH 15 ++#define DT_SYMBOLIC 16 ++#define DT_REL 17 ++#define DT_RELSZ 18 ++#define DT_RELENT 19 ++#define DT_PLTREL 20 ++#define DT_DEBUG 21 ++#define DT_TEXTREL 22 ++#define DT_JMPREL 23 ++#define DT_BIND_NOW 24 ++#define DT_INIT_ARRAY 25 ++#define DT_FINI_ARRAY 26 ++#define DT_INIT_ARRAYSZ 27 ++#define DT_FINI_ARRAYSZ 28 ++#define DT_RUNPATH 29 ++#define DT_FLAGS 30 ++#define DT_ENCODING 32 ++#define DT_PREINIT_ARRAY 32 ++#define DT_PREINIT_ARRAYSZ 33 ++#define DT_MAXPOSTAGS 34 ++#define DT_LOOS 0x6000000D ++#define DT_HIOS 0x6ffff000 ++#define DT_LOPROC 0x70000000 ++#define DT_HIPROC 0x7FFFFFFF ++ ++// DT_FLAGS values ++#define DF_ORIGIN 0x1 ++#define DF_SYMBOLIC 0x2 ++#define DF_TEXTREL 0x4 ++#define DF_BIND_NOW 0x8 ++#define DF_STATIC_TLS 0x10 ++ ++// ELF file header ++struct Elf32_Ehdr ++{ ++ unsigned char e_ident[EI_NIDENT]; ++ Elf_Half e_type; ++ Elf_Half e_machine; ++ Elf_Word e_version; ++ Elf32_Addr e_entry; ++ Elf32_Off e_phoff; ++ Elf32_Off e_shoff; ++ Elf_Word e_flags; ++ Elf_Half e_ehsize; ++ Elf_Half e_phentsize; ++ Elf_Half e_phnum; ++ Elf_Half e_shentsize; ++ Elf_Half e_shnum; ++ Elf_Half e_shstrndx; ++}; ++ ++struct Elf64_Ehdr ++{ ++ unsigned char e_ident[EI_NIDENT]; ++ Elf_Half e_type; ++ Elf_Half e_machine; ++ Elf_Word e_version; ++ Elf64_Addr e_entry; ++ Elf64_Off e_phoff; ++ Elf64_Off e_shoff; ++ Elf_Word e_flags; ++ Elf_Half e_ehsize; ++ Elf_Half e_phentsize; ++ Elf_Half e_phnum; ++ Elf_Half e_shentsize; ++ Elf_Half e_shnum; ++ Elf_Half e_shstrndx; ++}; ++ ++// Section header ++struct Elf32_Shdr ++{ ++ Elf_Word sh_name; ++ Elf_Word sh_type; ++ Elf_Word sh_flags; ++ Elf32_Addr sh_addr; ++ Elf32_Off sh_offset; ++ Elf_Word sh_size; ++ Elf_Word sh_link; ++ Elf_Word sh_info; ++ Elf_Word sh_addralign; ++ Elf_Word sh_entsize; ++}; ++ ++struct Elf64_Shdr ++{ ++ Elf_Word sh_name; ++ Elf_Word sh_type; ++ Elf_Xword sh_flags; ++ Elf64_Addr sh_addr; ++ Elf64_Off sh_offset; ++ Elf_Xword sh_size; ++ Elf_Word sh_link; ++ Elf_Word sh_info; ++ Elf_Xword sh_addralign; ++ Elf_Xword sh_entsize; ++}; ++ ++// Segment header ++struct Elf32_Phdr ++{ ++ Elf_Word p_type; ++ Elf32_Off p_offset; ++ Elf32_Addr p_vaddr; ++ Elf32_Addr p_paddr; ++ Elf_Word p_filesz; ++ Elf_Word p_memsz; ++ Elf_Word p_flags; ++ Elf_Word p_align; ++}; ++ ++struct Elf64_Phdr ++{ ++ Elf_Word p_type; ++ Elf_Word p_flags; ++ Elf64_Off p_offset; ++ Elf64_Addr p_vaddr; ++ Elf64_Addr p_paddr; ++ Elf_Xword p_filesz; ++ Elf_Xword p_memsz; ++ Elf_Xword p_align; ++}; ++ ++// Symbol table entry ++struct Elf32_Sym ++{ ++ Elf_Word st_name; ++ Elf32_Addr st_value; ++ Elf_Word st_size; ++ unsigned char st_info; ++ unsigned char st_other; ++ Elf_Half st_shndx; ++}; ++ ++struct Elf64_Sym ++{ ++ Elf_Word st_name; ++ unsigned char st_info; ++ unsigned char st_other; ++ Elf_Half st_shndx; ++ Elf64_Addr st_value; ++ Elf_Xword st_size; ++}; ++ ++#define ELF_ST_BIND( i ) ( ( i ) >> 4 ) ++#define ELF_ST_TYPE( i ) ( (i)&0xf ) ++#define ELF_ST_INFO( b, t ) ( ( ( b ) << 4 ) + ( (t)&0xf ) ) ++ ++#define ELF_ST_VISIBILITY( o ) ( (o)&0x3 ) ++ ++// Relocation entries ++struct Elf32_Rel ++{ ++ Elf32_Addr r_offset; ++ Elf_Word r_info; ++}; ++ ++struct Elf32_Rela ++{ ++ Elf32_Addr r_offset; ++ Elf_Word r_info; ++ Elf_Sword r_addend; ++}; ++ ++struct Elf64_Rel ++{ ++ Elf64_Addr r_offset; ++ Elf_Xword r_info; ++}; ++ ++struct Elf64_Rela ++{ ++ Elf64_Addr r_offset; ++ Elf_Xword r_info; ++ Elf_Sxword r_addend; ++}; ++ ++#define ELF32_R_SYM( i ) ( ( i ) >> 8 ) ++#define ELF32_R_TYPE( i ) ( (unsigned char)( i ) ) ++#define ELF32_R_INFO( s, t ) ( ( ( s ) << 8 ) + (unsigned char)( t ) ) ++ ++#define ELF64_R_SYM( i ) ( ( i ) >> 32 ) ++#define ELF64_R_TYPE( i ) ( (i)&0xffffffffL ) ++#define ELF64_R_INFO( s, t ) \ ++ ( ( ( ( int64_t )( s ) ) << 32 ) + ( (t)&0xffffffffL ) ) ++ ++// Dynamic structure ++struct Elf32_Dyn ++{ ++ Elf_Sword d_tag; ++ union { ++ Elf_Word d_val; ++ Elf32_Addr d_ptr; ++ } d_un; ++}; ++ ++struct Elf64_Dyn ++{ ++ Elf_Sxword d_tag; ++ union { ++ Elf_Xword d_val; ++ Elf64_Addr d_ptr; ++ } d_un; ++}; ++ ++} // namespace ELFIO ++ ++#endif // ELFTYPES_H ++ ++/*** End of inlined file: elf_types.hpp ***/ ++ ++ ++/*** Start of inlined file: elfio_version.hpp ***/ ++#define ELFIO_VERSION "3.8" ++ ++/*** End of inlined file: elfio_version.hpp ***/ ++ ++ ++/*** Start of inlined file: elfio_utils.hpp ***/ ++#ifndef ELFIO_UTILS_HPP ++#define ELFIO_UTILS_HPP ++ ++#define ELFIO_GET_ACCESS( TYPE, NAME, FIELD ) \ ++ TYPE get_##NAME() const { return ( *convertor )( FIELD ); } ++#define ELFIO_SET_ACCESS( TYPE, NAME, FIELD ) \ ++ void set_##NAME( TYPE value ) \ ++ { \ ++ FIELD = value; \ ++ FIELD = ( *convertor )( FIELD ); \ ++ } ++#define ELFIO_GET_SET_ACCESS( TYPE, NAME, FIELD ) \ ++ TYPE get_##NAME() const { return ( *convertor )( FIELD ); } \ ++ void set_##NAME( TYPE value ) \ ++ { \ ++ FIELD = value; \ ++ FIELD = ( *convertor )( FIELD ); \ ++ } ++ ++#define ELFIO_GET_ACCESS_DECL( TYPE, NAME ) virtual TYPE get_##NAME() const = 0 ++ ++#define ELFIO_SET_ACCESS_DECL( TYPE, NAME ) \ ++ virtual void set_##NAME( TYPE value ) = 0 ++ ++#define ELFIO_GET_SET_ACCESS_DECL( TYPE, NAME ) \ ++ virtual TYPE get_##NAME() const = 0; \ ++ virtual void set_##NAME( TYPE value ) = 0 ++ ++namespace ELFIO { ++ ++//------------------------------------------------------------------------------ ++class endianess_convertor ++{ ++ public: ++ //------------------------------------------------------------------------------ ++ endianess_convertor() { need_conversion = false; } ++ ++ //------------------------------------------------------------------------------ ++ void setup( unsigned char elf_file_encoding ) ++ { ++ need_conversion = ( elf_file_encoding != get_host_encoding() ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ uint64_t operator()( uint64_t value ) const ++ { ++ if ( !need_conversion ) { ++ return value; ++ } ++ value = ( ( value & 0x00000000000000FFull ) << 56 ) | ++ ( ( value & 0x000000000000FF00ull ) << 40 ) | ++ ( ( value & 0x0000000000FF0000ull ) << 24 ) | ++ ( ( value & 0x00000000FF000000ull ) << 8 ) | ++ ( ( value & 0x000000FF00000000ull ) >> 8 ) | ++ ( ( value & 0x0000FF0000000000ull ) >> 24 ) | ++ ( ( value & 0x00FF000000000000ull ) >> 40 ) | ++ ( ( value & 0xFF00000000000000ull ) >> 56 ); ++ ++ return value; ++ } ++ ++ //------------------------------------------------------------------------------ ++ int64_t operator()( int64_t value ) const ++ { ++ if ( !need_conversion ) { ++ return value; ++ } ++ return ( int64_t )( *this )( (uint64_t)value ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ uint32_t operator()( uint32_t value ) const ++ { ++ if ( !need_conversion ) { ++ return value; ++ } ++ value = ++ ( ( value & 0x000000FF ) << 24 ) | ( ( value & 0x0000FF00 ) << 8 ) | ++ ( ( value & 0x00FF0000 ) >> 8 ) | ( ( value & 0xFF000000 ) >> 24 ); ++ ++ return value; ++ } ++ ++ //------------------------------------------------------------------------------ ++ int32_t operator()( int32_t value ) const ++ { ++ if ( !need_conversion ) { ++ return value; ++ } ++ return ( int32_t )( *this )( (uint32_t)value ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ uint16_t operator()( uint16_t value ) const ++ { ++ if ( !need_conversion ) { ++ return value; ++ } ++ value = ( ( value & 0x00FF ) << 8 ) | ( ( value & 0xFF00 ) >> 8 ); ++ ++ return value; ++ } ++ ++ //------------------------------------------------------------------------------ ++ int16_t operator()( int16_t value ) const ++ { ++ if ( !need_conversion ) { ++ return value; ++ } ++ return ( int16_t )( *this )( (uint16_t)value ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ int8_t operator()( int8_t value ) const { return value; } ++ ++ //------------------------------------------------------------------------------ ++ uint8_t operator()( uint8_t value ) const { return value; } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ //------------------------------------------------------------------------------ ++ unsigned char get_host_encoding() const ++ { ++ static const int tmp = 1; ++ if ( 1 == *(const char*)&tmp ) { ++ return ELFDATA2LSB; ++ } ++ else { ++ return ELFDATA2MSB; ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ bool need_conversion; ++}; ++ ++//------------------------------------------------------------------------------ ++inline uint32_t elf_hash( const unsigned char* name ) ++{ ++ uint32_t h = 0, g; ++ while ( *name ) { ++ h = ( h << 4 ) + *name++; ++ g = h & 0xf0000000; ++ if ( g != 0 ) ++ h ^= g >> 24; ++ h &= ~g; ++ } ++ return h; ++} ++ ++} // namespace ELFIO ++ ++#endif // ELFIO_UTILS_HPP ++ ++/*** End of inlined file: elfio_utils.hpp ***/ ++ ++ ++/*** Start of inlined file: elfio_header.hpp ***/ ++#ifndef ELF_HEADER_HPP ++#define ELF_HEADER_HPP ++ ++#include <iostream> ++ ++namespace ELFIO { ++ ++class elf_header ++{ ++ public: ++ virtual ~elf_header(){}; ++ virtual bool load( std::istream& stream ) = 0; ++ virtual bool save( std::ostream& stream ) const = 0; ++ ++ // ELF header functions ++ ELFIO_GET_ACCESS_DECL( unsigned char, class ); ++ ELFIO_GET_ACCESS_DECL( unsigned char, elf_version ); ++ ELFIO_GET_ACCESS_DECL( unsigned char, encoding ); ++ ELFIO_GET_ACCESS_DECL( Elf_Half, header_size ); ++ ELFIO_GET_ACCESS_DECL( Elf_Half, section_entry_size ); ++ ELFIO_GET_ACCESS_DECL( Elf_Half, segment_entry_size ); ++ ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Word, version ); ++ ELFIO_GET_SET_ACCESS_DECL( unsigned char, os_abi ); ++ ELFIO_GET_SET_ACCESS_DECL( unsigned char, abi_version ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Half, type ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Half, machine ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Word, flags ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, entry ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Half, sections_num ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf64_Off, sections_offset ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Half, segments_num ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf64_Off, segments_offset ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Half, section_name_str_index ); ++}; ++ ++template <class T> struct elf_header_impl_types; ++template <> struct elf_header_impl_types<Elf32_Ehdr> ++{ ++ typedef Elf32_Phdr Phdr_type; ++ typedef Elf32_Shdr Shdr_type; ++ static const unsigned char file_class = ELFCLASS32; ++}; ++template <> struct elf_header_impl_types<Elf64_Ehdr> ++{ ++ typedef Elf64_Phdr Phdr_type; ++ typedef Elf64_Shdr Shdr_type; ++ static const unsigned char file_class = ELFCLASS64; ++}; ++ ++template <class T> class elf_header_impl : public elf_header ++{ ++ public: ++ //------------------------------------------------------------------------------ ++ elf_header_impl( endianess_convertor* convertor_, unsigned char encoding ) ++ { ++ convertor = convertor_; ++ ++ std::fill_n( reinterpret_cast<char*>( &header ), sizeof( header ), ++ '\0' ); ++ ++ header.e_ident[EI_MAG0] = ELFMAG0; ++ header.e_ident[EI_MAG1] = ELFMAG1; ++ header.e_ident[EI_MAG2] = ELFMAG2; ++ header.e_ident[EI_MAG3] = ELFMAG3; ++ header.e_ident[EI_CLASS] = elf_header_impl_types<T>::file_class; ++ header.e_ident[EI_DATA] = encoding; ++ header.e_ident[EI_VERSION] = EV_CURRENT; ++ header.e_version = ( *convertor )( (Elf_Word)EV_CURRENT ); ++ header.e_ehsize = ( sizeof( header ) ); ++ header.e_ehsize = ( *convertor )( header.e_ehsize ); ++ header.e_shstrndx = ( *convertor )( (Elf_Half)1 ); ++ header.e_phentsize = ++ sizeof( typename elf_header_impl_types<T>::Phdr_type ); ++ header.e_shentsize = ++ sizeof( typename elf_header_impl_types<T>::Shdr_type ); ++ header.e_phentsize = ( *convertor )( header.e_phentsize ); ++ header.e_shentsize = ( *convertor )( header.e_shentsize ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool load( std::istream& stream ) ++ { ++ stream.seekg( 0 ); ++ stream.read( reinterpret_cast<char*>( &header ), sizeof( header ) ); ++ ++ return ( stream.gcount() == sizeof( header ) ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool save( std::ostream& stream ) const ++ { ++ stream.seekp( 0 ); ++ stream.write( reinterpret_cast<const char*>( &header ), ++ sizeof( header ) ); ++ ++ return stream.good(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ // ELF header functions ++ ELFIO_GET_ACCESS( unsigned char, class, header.e_ident[EI_CLASS] ); ++ ELFIO_GET_ACCESS( unsigned char, elf_version, header.e_ident[EI_VERSION] ); ++ ELFIO_GET_ACCESS( unsigned char, encoding, header.e_ident[EI_DATA] ); ++ ELFIO_GET_ACCESS( Elf_Half, header_size, header.e_ehsize ); ++ ELFIO_GET_ACCESS( Elf_Half, section_entry_size, header.e_shentsize ); ++ ELFIO_GET_ACCESS( Elf_Half, segment_entry_size, header.e_phentsize ); ++ ++ ELFIO_GET_SET_ACCESS( Elf_Word, version, header.e_version ); ++ ELFIO_GET_SET_ACCESS( unsigned char, os_abi, header.e_ident[EI_OSABI] ); ++ ELFIO_GET_SET_ACCESS( unsigned char, ++ abi_version, ++ header.e_ident[EI_ABIVERSION] ); ++ ELFIO_GET_SET_ACCESS( Elf_Half, type, header.e_type ); ++ ELFIO_GET_SET_ACCESS( Elf_Half, machine, header.e_machine ); ++ ELFIO_GET_SET_ACCESS( Elf_Word, flags, header.e_flags ); ++ ELFIO_GET_SET_ACCESS( Elf_Half, section_name_str_index, header.e_shstrndx ); ++ ELFIO_GET_SET_ACCESS( Elf64_Addr, entry, header.e_entry ); ++ ELFIO_GET_SET_ACCESS( Elf_Half, sections_num, header.e_shnum ); ++ ELFIO_GET_SET_ACCESS( Elf64_Off, sections_offset, header.e_shoff ); ++ ELFIO_GET_SET_ACCESS( Elf_Half, segments_num, header.e_phnum ); ++ ELFIO_GET_SET_ACCESS( Elf64_Off, segments_offset, header.e_phoff ); ++ ++ private: ++ T header; ++ endianess_convertor* convertor; ++}; ++ ++} // namespace ELFIO ++ ++#endif // ELF_HEADER_HPP ++ ++/*** End of inlined file: elfio_header.hpp ***/ ++ ++ ++/*** Start of inlined file: elfio_section.hpp ***/ ++#ifndef ELFIO_SECTION_HPP ++#define ELFIO_SECTION_HPP ++ ++#include <string> ++#include <iostream> ++#include <new> ++ ++namespace ELFIO { ++ ++class section ++{ ++ friend class elfio; ++ ++ public: ++ virtual ~section(){}; ++ ++ ELFIO_GET_ACCESS_DECL( Elf_Half, index ); ++ ELFIO_GET_SET_ACCESS_DECL( std::string, name ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Word, type ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, flags ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Word, info ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Word, link ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, addr_align ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, entry_size ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, address ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, size ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Word, name_string_offset ); ++ ELFIO_GET_ACCESS_DECL( Elf64_Off, offset ); ++ ++ virtual const char* get_data() const = 0; ++ virtual void set_data( const char* pData, Elf_Word size ) = 0; ++ virtual void set_data( const std::string& data ) = 0; ++ virtual void append_data( const char* pData, Elf_Word size ) = 0; ++ virtual void append_data( const std::string& data ) = 0; ++ virtual size_t get_stream_size() const = 0; ++ virtual void set_stream_size( size_t value ) = 0; ++ ++ protected: ++ ELFIO_SET_ACCESS_DECL( Elf64_Off, offset ); ++ ELFIO_SET_ACCESS_DECL( Elf_Half, index ); ++ ++ virtual void load( std::istream& stream, std::streampos header_offset ) = 0; ++ virtual void save( std::ostream& stream, ++ std::streampos header_offset, ++ std::streampos data_offset ) = 0; ++ virtual bool is_address_initialized() const = 0; ++}; ++ ++template <class T> class section_impl : public section ++{ ++ public: ++ //------------------------------------------------------------------------------ ++ section_impl( const endianess_convertor* convertor_ ) ++ : convertor( convertor_ ) ++ { ++ std::fill_n( reinterpret_cast<char*>( &header ), sizeof( header ), ++ '\0' ); ++ is_address_set = false; ++ data = 0; ++ data_size = 0; ++ index = 0; ++ stream_size = 0; ++ } ++ ++ //------------------------------------------------------------------------------ ++ ~section_impl() { delete[] data; } ++ ++ //------------------------------------------------------------------------------ ++ // Section info functions ++ ELFIO_GET_SET_ACCESS( Elf_Word, type, header.sh_type ); ++ ELFIO_GET_SET_ACCESS( Elf_Xword, flags, header.sh_flags ); ++ ELFIO_GET_SET_ACCESS( Elf_Xword, size, header.sh_size ); ++ ELFIO_GET_SET_ACCESS( Elf_Word, link, header.sh_link ); ++ ELFIO_GET_SET_ACCESS( Elf_Word, info, header.sh_info ); ++ ELFIO_GET_SET_ACCESS( Elf_Xword, addr_align, header.sh_addralign ); ++ ELFIO_GET_SET_ACCESS( Elf_Xword, entry_size, header.sh_entsize ); ++ ELFIO_GET_SET_ACCESS( Elf_Word, name_string_offset, header.sh_name ); ++ ELFIO_GET_ACCESS( Elf64_Addr, address, header.sh_addr ); ++ ++ //------------------------------------------------------------------------------ ++ Elf_Half get_index() const { return index; } ++ ++ //------------------------------------------------------------------------------ ++ std::string get_name() const { return name; } ++ ++ //------------------------------------------------------------------------------ ++ void set_name( std::string name_ ) { name = name_; } ++ ++ //------------------------------------------------------------------------------ ++ void set_address( Elf64_Addr value ) ++ { ++ header.sh_addr = value; ++ header.sh_addr = ( *convertor )( header.sh_addr ); ++ is_address_set = true; ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool is_address_initialized() const { return is_address_set; } ++ ++ //------------------------------------------------------------------------------ ++ const char* get_data() const { return data; } ++ ++ //------------------------------------------------------------------------------ ++ void set_data( const char* raw_data, Elf_Word size ) ++ { ++ if ( get_type() != SHT_NOBITS ) { ++ delete[] data; ++ data = new ( std::nothrow ) char[size]; ++ if ( 0 != data && 0 != raw_data ) { ++ data_size = size; ++ std::copy( raw_data, raw_data + size, data ); ++ } ++ else { ++ data_size = 0; ++ } ++ } ++ ++ set_size( data_size ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ void set_data( const std::string& str_data ) ++ { ++ return set_data( str_data.c_str(), (Elf_Word)str_data.size() ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ void append_data( const char* raw_data, Elf_Word size ) ++ { ++ if ( get_type() != SHT_NOBITS ) { ++ if ( get_size() + size < data_size ) { ++ std::copy( raw_data, raw_data + size, data + get_size() ); ++ } ++ else { ++ data_size = 2 * ( data_size + size ); ++ char* new_data = new ( std::nothrow ) char[data_size]; ++ ++ if ( 0 != new_data ) { ++ std::copy( data, data + get_size(), new_data ); ++ std::copy( raw_data, raw_data + size, ++ new_data + get_size() ); ++ delete[] data; ++ data = new_data; ++ } ++ else { ++ size = 0; ++ } ++ } ++ set_size( get_size() + size ); ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ void append_data( const std::string& str_data ) ++ { ++ return append_data( str_data.c_str(), (Elf_Word)str_data.size() ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ protected: ++ //------------------------------------------------------------------------------ ++ ELFIO_GET_SET_ACCESS( Elf64_Off, offset, header.sh_offset ); ++ ++ //------------------------------------------------------------------------------ ++ void set_index( Elf_Half value ) { index = value; } ++ ++ //------------------------------------------------------------------------------ ++ void load( std::istream& stream, std::streampos header_offset ) ++ { ++ std::fill_n( reinterpret_cast<char*>( &header ), sizeof( header ), ++ '\0' ); ++ ++ stream.seekg( 0, stream.end ); ++ set_stream_size( stream.tellg() ); ++ ++ stream.seekg( header_offset ); ++ stream.read( reinterpret_cast<char*>( &header ), sizeof( header ) ); ++ ++ Elf_Xword size = get_size(); ++ if ( 0 == data && SHT_NULL != get_type() && SHT_NOBITS != get_type() && ++ size < get_stream_size() ) { ++ data = new ( std::nothrow ) char[size + 1]; ++ ++ if ( ( 0 != size ) && ( 0 != data ) ) { ++ stream.seekg( ( *convertor )( header.sh_offset ) ); ++ stream.read( data, size ); ++ data[size] = 0; // Ensure data is ended with 0 to avoid oob read ++ data_size = size; ++ } ++ else { ++ data_size = 0; ++ } ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ void save( std::ostream& stream, ++ std::streampos header_offset, ++ std::streampos data_offset ) ++ { ++ if ( 0 != get_index() ) { ++ header.sh_offset = data_offset; ++ header.sh_offset = ( *convertor )( header.sh_offset ); ++ } ++ ++ save_header( stream, header_offset ); ++ if ( get_type() != SHT_NOBITS && get_type() != SHT_NULL && ++ get_size() != 0 && data != 0 ) { ++ save_data( stream, data_offset ); ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ //------------------------------------------------------------------------------ ++ void save_header( std::ostream& stream, std::streampos header_offset ) const ++ { ++ stream.seekp( header_offset ); ++ stream.write( reinterpret_cast<const char*>( &header ), ++ sizeof( header ) ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ void save_data( std::ostream& stream, std::streampos data_offset ) const ++ { ++ stream.seekp( data_offset ); ++ stream.write( get_data(), get_size() ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ size_t get_stream_size() const { return stream_size; } ++ ++ //------------------------------------------------------------------------------ ++ void set_stream_size( size_t value ) { stream_size = value; } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ T header; ++ Elf_Half index; ++ std::string name; ++ char* data; ++ Elf_Word data_size; ++ const endianess_convertor* convertor; ++ bool is_address_set; ++ size_t stream_size; ++}; ++ ++} // namespace ELFIO ++ ++#endif // ELFIO_SECTION_HPP ++ ++/*** End of inlined file: elfio_section.hpp ***/ ++ ++ ++/*** Start of inlined file: elfio_segment.hpp ***/ ++#ifndef ELFIO_SEGMENT_HPP ++#define ELFIO_SEGMENT_HPP ++ ++#include <iostream> ++#include <vector> ++#include <new> ++ ++namespace ELFIO { ++ ++class segment ++{ ++ friend class elfio; ++ ++ public: ++ virtual ~segment(){}; ++ ++ ELFIO_GET_ACCESS_DECL( Elf_Half, index ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Word, type ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Word, flags ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, align ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, virtual_address ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, physical_address ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, file_size ); ++ ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, memory_size ); ++ ELFIO_GET_ACCESS_DECL( Elf64_Off, offset ); ++ ++ virtual const char* get_data() const = 0; ++ ++ virtual Elf_Half add_section_index( Elf_Half index, ++ Elf_Xword addr_align ) = 0; ++ virtual Elf_Half get_sections_num() const = 0; ++ virtual Elf_Half get_section_index_at( Elf_Half num ) const = 0; ++ virtual bool is_offset_initialized() const = 0; ++ ++ protected: ++ ELFIO_SET_ACCESS_DECL( Elf64_Off, offset ); ++ ELFIO_SET_ACCESS_DECL( Elf_Half, index ); ++ ++ virtual const std::vector<Elf_Half>& get_sections() const = 0; ++ virtual void load( std::istream& stream, std::streampos header_offset ) = 0; ++ virtual void save( std::ostream& stream, ++ std::streampos header_offset, ++ std::streampos data_offset ) = 0; ++}; ++ ++//------------------------------------------------------------------------------ ++template <class T> class segment_impl : public segment ++{ ++ public: ++ //------------------------------------------------------------------------------ ++ segment_impl( endianess_convertor* convertor_ ) ++ : stream_size( 0 ), index( 0 ), data( 0 ), convertor( convertor_ ) ++ { ++ is_offset_set = false; ++ std::fill_n( reinterpret_cast<char*>( &ph ), sizeof( ph ), '\0' ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ virtual ~segment_impl() { delete[] data; } ++ ++ //------------------------------------------------------------------------------ ++ // Section info functions ++ ELFIO_GET_SET_ACCESS( Elf_Word, type, ph.p_type ); ++ ELFIO_GET_SET_ACCESS( Elf_Word, flags, ph.p_flags ); ++ ELFIO_GET_SET_ACCESS( Elf_Xword, align, ph.p_align ); ++ ELFIO_GET_SET_ACCESS( Elf64_Addr, virtual_address, ph.p_vaddr ); ++ ELFIO_GET_SET_ACCESS( Elf64_Addr, physical_address, ph.p_paddr ); ++ ELFIO_GET_SET_ACCESS( Elf_Xword, file_size, ph.p_filesz ); ++ ELFIO_GET_SET_ACCESS( Elf_Xword, memory_size, ph.p_memsz ); ++ ELFIO_GET_ACCESS( Elf64_Off, offset, ph.p_offset ); ++ size_t stream_size; ++ ++ //------------------------------------------------------------------------------ ++ size_t get_stream_size() const { return stream_size; } ++ ++ //------------------------------------------------------------------------------ ++ void set_stream_size( size_t value ) { stream_size = value; } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Half get_index() const { return index; } ++ ++ //------------------------------------------------------------------------------ ++ const char* get_data() const { return data; } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Half add_section_index( Elf_Half sec_index, Elf_Xword addr_align ) ++ { ++ sections.push_back( sec_index ); ++ if ( addr_align > get_align() ) { ++ set_align( addr_align ); ++ } ++ ++ return (Elf_Half)sections.size(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Half get_sections_num() const { return (Elf_Half)sections.size(); } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Half get_section_index_at( Elf_Half num ) const ++ { ++ if ( num < sections.size() ) { ++ return sections[num]; ++ } ++ ++ return Elf_Half( -1 ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ protected: ++ //------------------------------------------------------------------------------ ++ ++ //------------------------------------------------------------------------------ ++ void set_offset( Elf64_Off value ) ++ { ++ ph.p_offset = value; ++ ph.p_offset = ( *convertor )( ph.p_offset ); ++ is_offset_set = true; ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool is_offset_initialized() const { return is_offset_set; } ++ ++ //------------------------------------------------------------------------------ ++ const std::vector<Elf_Half>& get_sections() const { return sections; } ++ ++ //------------------------------------------------------------------------------ ++ void set_index( Elf_Half value ) { index = value; } ++ ++ //------------------------------------------------------------------------------ ++ void load( std::istream& stream, std::streampos header_offset ) ++ { ++ ++ stream.seekg( 0, stream.end ); ++ set_stream_size( stream.tellg() ); ++ ++ stream.seekg( header_offset ); ++ stream.read( reinterpret_cast<char*>( &ph ), sizeof( ph ) ); ++ is_offset_set = true; ++ ++ if ( PT_NULL != get_type() && 0 != get_file_size() ) { ++ stream.seekg( ( *convertor )( ph.p_offset ) ); ++ Elf_Xword size = get_file_size(); ++ ++ if ( size > get_stream_size() ) { ++ data = 0; ++ } ++ else { ++ data = new (std::nothrow) char[size + 1]; ++ ++ if ( 0 != data ) { ++ stream.read( data, size ); ++ data[size] = 0; ++ } ++ } ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ void save( std::ostream& stream, ++ std::streampos header_offset, ++ std::streampos data_offset ) ++ { ++ ph.p_offset = data_offset; ++ ph.p_offset = ( *convertor )( ph.p_offset ); ++ stream.seekp( header_offset ); ++ stream.write( reinterpret_cast<const char*>( &ph ), sizeof( ph ) ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ T ph; ++ Elf_Half index; ++ char* data; ++ std::vector<Elf_Half> sections; ++ endianess_convertor* convertor; ++ bool is_offset_set; ++}; ++ ++} // namespace ELFIO ++ ++#endif // ELFIO_SEGMENT_HPP ++ ++/*** End of inlined file: elfio_segment.hpp ***/ ++ ++ ++/*** Start of inlined file: elfio_strings.hpp ***/ ++#ifndef ELFIO_STRINGS_HPP ++#define ELFIO_STRINGS_HPP ++ ++#include <cstdlib> ++#include <cstring> ++#include <string> ++ ++namespace ELFIO { ++ ++//------------------------------------------------------------------------------ ++template <class S> class string_section_accessor_template ++{ ++ public: ++ //------------------------------------------------------------------------------ ++ string_section_accessor_template( S* section_ ) : string_section( section_ ) ++ { ++ } ++ ++ //------------------------------------------------------------------------------ ++ const char* get_string( Elf_Word index ) const ++ { ++ if ( string_section ) { ++ if ( index < string_section->get_size() ) { ++ const char* data = string_section->get_data(); ++ if ( 0 != data ) { ++ return data + index; ++ } ++ } ++ } ++ ++ return 0; ++ } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Word add_string( const char* str ) ++ { ++ Elf_Word current_position = 0; ++ ++ if ( string_section ) { ++ // Strings are addeded to the end of the current section data ++ current_position = (Elf_Word)string_section->get_size(); ++ ++ if ( current_position == 0 ) { ++ char empty_string = '\0'; ++ string_section->append_data( &empty_string, 1 ); ++ current_position++; ++ } ++ string_section->append_data( str, ++ (Elf_Word)std::strlen( str ) + 1 ); ++ } ++ ++ return current_position; ++ } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Word add_string( const std::string& str ) ++ { ++ return add_string( str.c_str() ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ S* string_section; ++}; ++ ++using string_section_accessor = string_section_accessor_template<section>; ++using const_string_section_accessor = ++ string_section_accessor_template<const section>; ++ ++} // namespace ELFIO ++ ++#endif // ELFIO_STRINGS_HPP ++ ++/*** End of inlined file: elfio_strings.hpp ***/ ++ ++#define ELFIO_HEADER_ACCESS_GET( TYPE, FNAME ) \ ++ TYPE get_##FNAME() const { return header ? ( header->get_##FNAME() ) : 0; } ++ ++#define ELFIO_HEADER_ACCESS_GET_SET( TYPE, FNAME ) \ ++ TYPE get_##FNAME() const \ ++ { \ ++ return header ? ( header->get_##FNAME() ) : 0; \ ++ } \ ++ void set_##FNAME( TYPE val ) \ ++ { \ ++ if ( header ) { \ ++ header->set_##FNAME( val ); \ ++ } \ ++ } ++ ++namespace ELFIO { ++ ++//------------------------------------------------------------------------------ ++class elfio ++{ ++ public: ++ //------------------------------------------------------------------------------ ++ elfio() : sections( this ), segments( this ) ++ { ++ header = 0; ++ current_file_pos = 0; ++ create( ELFCLASS32, ELFDATA2LSB ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ ~elfio() { clean(); } ++ ++ //------------------------------------------------------------------------------ ++ void create( unsigned char file_class, unsigned char encoding ) ++ { ++ clean(); ++ convertor.setup( encoding ); ++ header = create_header( file_class, encoding ); ++ create_mandatory_sections(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool load( const std::string& file_name ) ++ { ++ std::ifstream stream; ++ stream.open( file_name.c_str(), std::ios::in | std::ios::binary ); ++ if ( !stream ) { ++ return false; ++ } ++ ++ return load( stream ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool load( std::istream& stream ) ++ { ++ clean(); ++ ++ unsigned char e_ident[EI_NIDENT]; ++ // Read ELF file signature ++ stream.read( reinterpret_cast<char*>( &e_ident ), sizeof( e_ident ) ); ++ ++ // Is it ELF file? ++ if ( stream.gcount() != sizeof( e_ident ) || ++ e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 || ++ e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3 ) { ++ return false; ++ } ++ ++ if ( ( e_ident[EI_CLASS] != ELFCLASS64 ) && ++ ( e_ident[EI_CLASS] != ELFCLASS32 ) ) { ++ return false; ++ } ++ ++ convertor.setup( e_ident[EI_DATA] ); ++ header = create_header( e_ident[EI_CLASS], e_ident[EI_DATA] ); ++ if ( 0 == header ) { ++ return false; ++ } ++ if ( !header->load( stream ) ) { ++ return false; ++ } ++ ++ load_sections( stream ); ++ bool is_still_good = load_segments( stream ); ++ return is_still_good; ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool save( const std::string& file_name ) ++ { ++ std::ofstream stream; ++ stream.open( file_name.c_str(), std::ios::out | std::ios::binary ); ++ if ( !stream ) { ++ return false; ++ } ++ ++ return save( stream ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool save( std::ostream& stream ) ++ { ++ if ( !stream || !header ) { ++ return false; ++ } ++ ++ bool is_still_good = true; ++ // Define layout specific header fields ++ // The position of the segment table is fixed after the header. ++ // The position of the section table is variable and needs to be fixed ++ // before saving. ++ header->set_segments_num( segments.size() ); ++ header->set_segments_offset( segments.size() ? header->get_header_size() ++ : 0 ); ++ header->set_sections_num( sections.size() ); ++ header->set_sections_offset( 0 ); ++ ++ // Layout the first section right after the segment table ++ current_file_pos = header->get_header_size() + ++ header->get_segment_entry_size() * ++ (Elf_Xword)header->get_segments_num(); ++ ++ calc_segment_alignment(); ++ ++ is_still_good = layout_segments_and_their_sections(); ++ is_still_good = is_still_good && layout_sections_without_segments(); ++ is_still_good = is_still_good && layout_section_table(); ++ ++ is_still_good = is_still_good && save_header( stream ); ++ is_still_good = is_still_good && save_sections( stream ); ++ is_still_good = is_still_good && save_segments( stream ); ++ ++ return is_still_good; ++ } ++ ++ //------------------------------------------------------------------------------ ++ // ELF header access functions ++ ELFIO_HEADER_ACCESS_GET( unsigned char, class ); ++ ELFIO_HEADER_ACCESS_GET( unsigned char, elf_version ); ++ ELFIO_HEADER_ACCESS_GET( unsigned char, encoding ); ++ ELFIO_HEADER_ACCESS_GET( Elf_Word, version ); ++ ELFIO_HEADER_ACCESS_GET( Elf_Half, header_size ); ++ ELFIO_HEADER_ACCESS_GET( Elf_Half, section_entry_size ); ++ ELFIO_HEADER_ACCESS_GET( Elf_Half, segment_entry_size ); ++ ++ ELFIO_HEADER_ACCESS_GET_SET( unsigned char, os_abi ); ++ ELFIO_HEADER_ACCESS_GET_SET( unsigned char, abi_version ); ++ ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, type ); ++ ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, machine ); ++ ELFIO_HEADER_ACCESS_GET_SET( Elf_Word, flags ); ++ ELFIO_HEADER_ACCESS_GET_SET( Elf64_Addr, entry ); ++ ELFIO_HEADER_ACCESS_GET_SET( Elf64_Off, sections_offset ); ++ ELFIO_HEADER_ACCESS_GET_SET( Elf64_Off, segments_offset ); ++ ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, section_name_str_index ); ++ ++ //------------------------------------------------------------------------------ ++ const endianess_convertor& get_convertor() const { return convertor; } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Xword get_default_entry_size( Elf_Word section_type ) const ++ { ++ switch ( section_type ) { ++ case SHT_RELA: ++ if ( header->get_class() == ELFCLASS64 ) { ++ return sizeof( Elf64_Rela ); ++ } ++ else { ++ return sizeof( Elf32_Rela ); ++ } ++ case SHT_REL: ++ if ( header->get_class() == ELFCLASS64 ) { ++ return sizeof( Elf64_Rel ); ++ } ++ else { ++ return sizeof( Elf32_Rel ); ++ } ++ case SHT_SYMTAB: ++ if ( header->get_class() == ELFCLASS64 ) { ++ return sizeof( Elf64_Sym ); ++ } ++ else { ++ return sizeof( Elf32_Sym ); ++ } ++ case SHT_DYNAMIC: ++ if ( header->get_class() == ELFCLASS64 ) { ++ return sizeof( Elf64_Dyn ); ++ } ++ else { ++ return sizeof( Elf32_Dyn ); ++ } ++ default: ++ return 0; ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ bool is_offset_in_section( Elf64_Off offset, const section* sec ) const ++ { ++ return ( offset >= sec->get_offset() ) && ++ ( offset < ( sec->get_offset() + sec->get_size() ) ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ public: ++ //! returns an empty string if no problems are detected, ++ //! or a string containing an error message if problems are found ++ std::string validate() const ++ { ++ ++ // check for overlapping sections in the file ++ for ( int i = 0; i < sections.size(); ++i ) { ++ for ( int j = i + 1; j < sections.size(); ++j ) { ++ const section* a = sections[i]; ++ const section* b = sections[j]; ++ if ( !( a->get_type() & SHT_NOBITS ) && ++ !( b->get_type() & SHT_NOBITS ) && ( a->get_size() > 0 ) && ++ ( b->get_size() > 0 ) && ( a->get_offset() > 0 ) && ++ ( b->get_offset() > 0 ) ) { ++ if ( is_offset_in_section( a->get_offset(), b ) || ++ is_offset_in_section( ++ a->get_offset() + a->get_size() - 1, b ) || ++ is_offset_in_section( b->get_offset(), a ) || ++ is_offset_in_section( ++ b->get_offset() + b->get_size() - 1, a ) ) { ++ return "Sections " + a->get_name() + " and " + ++ b->get_name() + " overlap in file"; ++ } ++ } ++ } ++ } ++ ++ // more checks to be added here... ++ ++ return ""; ++ } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ //------------------------------------------------------------------------------ ++ void clean() ++ { ++ delete header; ++ header = 0; ++ ++ std::vector<section*>::const_iterator it; ++ for ( it = sections_.begin(); it != sections_.end(); ++it ) { ++ delete *it; ++ } ++ sections_.clear(); ++ ++ std::vector<segment*>::const_iterator it1; ++ for ( it1 = segments_.begin(); it1 != segments_.end(); ++it1 ) { ++ delete *it1; ++ } ++ segments_.clear(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ elf_header* create_header( unsigned char file_class, ++ unsigned char encoding ) ++ { ++ elf_header* new_header = 0; ++ ++ if ( file_class == ELFCLASS64 ) { ++ new_header = ++ new elf_header_impl<Elf64_Ehdr>( &convertor, encoding ); ++ } ++ else if ( file_class == ELFCLASS32 ) { ++ new_header = ++ new elf_header_impl<Elf32_Ehdr>( &convertor, encoding ); ++ } ++ else { ++ return 0; ++ } ++ ++ return new_header; ++ } ++ ++ //------------------------------------------------------------------------------ ++ section* create_section() ++ { ++ section* new_section; ++ unsigned char file_class = get_class(); ++ ++ if ( file_class == ELFCLASS64 ) { ++ new_section = new section_impl<Elf64_Shdr>( &convertor ); ++ } ++ else if ( file_class == ELFCLASS32 ) { ++ new_section = new section_impl<Elf32_Shdr>( &convertor ); ++ } ++ else { ++ return 0; ++ } ++ ++ new_section->set_index( (Elf_Half)sections_.size() ); ++ sections_.push_back( new_section ); ++ ++ return new_section; ++ } ++ ++ //------------------------------------------------------------------------------ ++ segment* create_segment() ++ { ++ segment* new_segment; ++ unsigned char file_class = header->get_class(); ++ ++ if ( file_class == ELFCLASS64 ) { ++ new_segment = new segment_impl<Elf64_Phdr>( &convertor ); ++ } ++ else if ( file_class == ELFCLASS32 ) { ++ new_segment = new segment_impl<Elf32_Phdr>( &convertor ); ++ } ++ else { ++ return 0; ++ } ++ ++ new_segment->set_index( (Elf_Half)segments_.size() ); ++ segments_.push_back( new_segment ); ++ ++ return new_segment; ++ } ++ ++ //------------------------------------------------------------------------------ ++ void create_mandatory_sections() ++ { ++ // Create null section without calling to 'add_section' as no string ++ // section containing section names exists yet ++ section* sec0 = create_section(); ++ sec0->set_index( 0 ); ++ sec0->set_name( "" ); ++ sec0->set_name_string_offset( 0 ); ++ ++ set_section_name_str_index( 1 ); ++ section* shstrtab = sections.add( ".shstrtab" ); ++ shstrtab->set_type( SHT_STRTAB ); ++ shstrtab->set_addr_align( 1 ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Half load_sections( std::istream& stream ) ++ { ++ Elf_Half entry_size = header->get_section_entry_size(); ++ Elf_Half num = header->get_sections_num(); ++ Elf64_Off offset = header->get_sections_offset(); ++ ++ for ( Elf_Half i = 0; i < num; ++i ) { ++ section* sec = create_section(); ++ sec->load( stream, (std::streamoff)offset + ++ (std::streampos)i * entry_size ); ++ sec->set_index( i ); ++ // To mark that the section is not permitted to reassign address ++ // during layout calculation ++ sec->set_address( sec->get_address() ); ++ } ++ ++ Elf_Half shstrndx = get_section_name_str_index(); ++ ++ if ( SHN_UNDEF != shstrndx ) { ++ string_section_accessor str_reader( sections[shstrndx] ); ++ for ( Elf_Half i = 0; i < num; ++i ) { ++ Elf_Word section_offset = sections[i]->get_name_string_offset(); ++ const char* p = str_reader.get_string( section_offset ); ++ if ( p != 0 ) { ++ sections[i]->set_name( p ); ++ } ++ } ++ } ++ ++ return num; ++ } ++ ++ //------------------------------------------------------------------------------ ++ //! Checks whether the addresses of the section entirely fall within the given segment. ++ //! It doesn't matter if the addresses are memory addresses, or file offsets, ++ //! they just need to be in the same address space ++ bool is_sect_in_seg( Elf64_Off sect_begin, ++ Elf_Xword sect_size, ++ Elf64_Off seg_begin, ++ Elf64_Off seg_end ) ++ { ++ return ( seg_begin <= sect_begin ) && ++ ( sect_begin + sect_size <= seg_end ) && ++ ( sect_begin < ++ seg_end ); // this is important criteria when sect_size == 0 ++ // Example: seg_begin=10, seg_end=12 (-> covering the bytes 10 and 11) ++ // sect_begin=12, sect_size=0 -> shall return false! ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool load_segments( std::istream& stream ) ++ { ++ Elf_Half entry_size = header->get_segment_entry_size(); ++ Elf_Half num = header->get_segments_num(); ++ Elf64_Off offset = header->get_segments_offset(); ++ ++ for ( Elf_Half i = 0; i < num; ++i ) { ++ segment* seg; ++ unsigned char file_class = header->get_class(); ++ ++ if ( file_class == ELFCLASS64 ) { ++ seg = new segment_impl<Elf64_Phdr>( &convertor ); ++ } ++ else if ( file_class == ELFCLASS32 ) { ++ seg = new segment_impl<Elf32_Phdr>( &convertor ); ++ } ++ else { ++ return false; ++ } ++ ++ seg->load( stream, (std::streamoff)offset + ++ (std::streampos)i * entry_size ); ++ seg->set_index( i ); ++ ++ // Add sections to the segments (similar to readelfs algorithm) ++ Elf64_Off segBaseOffset = seg->get_offset(); ++ Elf64_Off segEndOffset = segBaseOffset + seg->get_file_size(); ++ Elf64_Off segVBaseAddr = seg->get_virtual_address(); ++ Elf64_Off segVEndAddr = segVBaseAddr + seg->get_memory_size(); ++ for ( Elf_Half j = 0; j < sections.size(); ++j ) { ++ const section* psec = sections[j]; ++ ++ // SHF_ALLOC sections are matched based on the virtual address ++ // otherwise the file offset is matched ++ if ( ( psec->get_flags() & SHF_ALLOC ) ++ ? is_sect_in_seg( psec->get_address(), ++ psec->get_size(), segVBaseAddr, ++ segVEndAddr ) ++ : is_sect_in_seg( psec->get_offset(), psec->get_size(), ++ segBaseOffset, segEndOffset ) ) { ++ // Alignment of segment shall not be updated, to preserve original value ++ // It will be re-calculated on saving. ++ seg->add_section_index( psec->get_index(), 0 ); ++ } ++ } ++ ++ // Add section into the segments' container ++ segments_.push_back( seg ); ++ } ++ ++ return true; ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool save_header( std::ostream& stream ) { return header->save( stream ); } ++ ++ //------------------------------------------------------------------------------ ++ bool save_sections( std::ostream& stream ) ++ { ++ for ( unsigned int i = 0; i < sections_.size(); ++i ) { ++ section* sec = sections_.at( i ); ++ ++ std::streampos headerPosition = ++ (std::streamoff)header->get_sections_offset() + ++ (std::streampos)header->get_section_entry_size() * ++ sec->get_index(); ++ ++ sec->save( stream, headerPosition, sec->get_offset() ); ++ } ++ return true; ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool save_segments( std::ostream& stream ) ++ { ++ for ( unsigned int i = 0; i < segments_.size(); ++i ) { ++ segment* seg = segments_.at( i ); ++ ++ std::streampos headerPosition = ++ header->get_segments_offset() + ++ (std::streampos)header->get_segment_entry_size() * ++ seg->get_index(); ++ ++ seg->save( stream, headerPosition, seg->get_offset() ); ++ } ++ return true; ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool is_section_without_segment( unsigned int section_index ) ++ { ++ bool found = false; ++ ++ for ( unsigned int j = 0; !found && ( j < segments.size() ); ++j ) { ++ for ( unsigned int k = 0; ++ !found && ( k < segments[j]->get_sections_num() ); ++k ) { ++ found = segments[j]->get_section_index_at( k ) == section_index; ++ } ++ } ++ ++ return !found; ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool is_subsequence_of( segment* seg1, segment* seg2 ) ++ { ++ // Return 'true' if sections of seg1 are a subset of sections in seg2 ++ const std::vector<Elf_Half>& sections1 = seg1->get_sections(); ++ const std::vector<Elf_Half>& sections2 = seg2->get_sections(); ++ ++ bool found = false; ++ if ( sections1.size() < sections2.size() ) { ++ found = std::includes( sections2.begin(), sections2.end(), ++ sections1.begin(), sections1.end() ); ++ } ++ ++ return found; ++ } ++ ++ //------------------------------------------------------------------------------ ++ std::vector<segment*> get_ordered_segments() ++ { ++ std::vector<segment*> res; ++ std::deque<segment*> worklist; ++ ++ res.reserve( segments.size() ); ++ std::copy( segments_.begin(), segments_.end(), ++ std::back_inserter( worklist ) ); ++ ++ // Bring the segments which start at address 0 to the front ++ size_t nextSlot = 0; ++ for ( size_t i = 0; i < worklist.size(); ++i ) { ++ if ( i != nextSlot && worklist[i]->is_offset_initialized() && ++ worklist[i]->get_offset() == 0 ) { ++ if ( worklist[nextSlot]->get_offset() == 0 ) { ++ ++nextSlot; ++ } ++ std::swap( worklist[i], worklist[nextSlot] ); ++ ++nextSlot; ++ } ++ } ++ ++ while ( !worklist.empty() ) { ++ segment* seg = worklist.front(); ++ worklist.pop_front(); ++ ++ size_t i = 0; ++ for ( ; i < worklist.size(); ++i ) { ++ if ( is_subsequence_of( seg, worklist[i] ) ) { ++ break; ++ } ++ } ++ ++ if ( i < worklist.size() ) ++ worklist.push_back( seg ); ++ else ++ res.push_back( seg ); ++ } ++ ++ return res; ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool layout_sections_without_segments() ++ { ++ for ( unsigned int i = 0; i < sections_.size(); ++i ) { ++ if ( is_section_without_segment( i ) ) { ++ section* sec = sections_[i]; ++ ++ Elf_Xword section_align = sec->get_addr_align(); ++ if ( section_align > 1 && ++ current_file_pos % section_align != 0 ) { ++ current_file_pos += ++ section_align - current_file_pos % section_align; ++ } ++ ++ if ( 0 != sec->get_index() ) ++ sec->set_offset( current_file_pos ); ++ ++ if ( SHT_NOBITS != sec->get_type() && ++ SHT_NULL != sec->get_type() ) { ++ current_file_pos += sec->get_size(); ++ } ++ } ++ } ++ ++ return true; ++ } ++ ++ //------------------------------------------------------------------------------ ++ void calc_segment_alignment() ++ { ++ for ( std::vector<segment*>::iterator s = segments_.begin(); ++ s != segments_.end(); ++s ) { ++ segment* seg = *s; ++ for ( int i = 0; i < seg->get_sections_num(); ++i ) { ++ section* sect = sections_[seg->get_section_index_at( i )]; ++ if ( sect->get_addr_align() > seg->get_align() ) { ++ seg->set_align( sect->get_addr_align() ); ++ } ++ } ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool layout_segments_and_their_sections() ++ { ++ std::vector<segment*> worklist; ++ std::vector<bool> section_generated( sections.size(), false ); ++ ++ // Get segments in a order in where segments which contain a ++ // sub sequence of other segments are located at the end ++ worklist = get_ordered_segments(); ++ ++ for ( unsigned int i = 0; i < worklist.size(); ++i ) { ++ Elf_Xword segment_memory = 0; ++ Elf_Xword segment_filesize = 0; ++ Elf_Xword seg_start_pos = current_file_pos; ++ segment* seg = worklist[i]; ++ ++ // Special case: PHDR segment ++ // This segment contains the program headers but no sections ++ if ( seg->get_type() == PT_PHDR && seg->get_sections_num() == 0 ) { ++ seg_start_pos = header->get_segments_offset(); ++ segment_memory = segment_filesize = ++ header->get_segment_entry_size() * ++ (Elf_Xword)header->get_segments_num(); ++ } ++ // Special case: ++ else if ( seg->is_offset_initialized() && seg->get_offset() == 0 ) { ++ seg_start_pos = 0; ++ if ( seg->get_sections_num() ) { ++ segment_memory = segment_filesize = current_file_pos; ++ } ++ } ++ // New segments with not generated sections ++ // have to be aligned ++ else if ( seg->get_sections_num() && ++ !section_generated[seg->get_section_index_at( 0 )] ) { ++ Elf_Xword align = seg->get_align() > 0 ? seg->get_align() : 1; ++ Elf64_Off cur_page_alignment = current_file_pos % align; ++ Elf64_Off req_page_alignment = ++ seg->get_virtual_address() % align; ++ Elf64_Off error = req_page_alignment - cur_page_alignment; ++ ++ current_file_pos += ( seg->get_align() + error ) % align; ++ seg_start_pos = current_file_pos; ++ } ++ else if ( seg->get_sections_num() ) { ++ seg_start_pos = ++ sections[seg->get_section_index_at( 0 )]->get_offset(); ++ } ++ ++ // Write segment's data ++ for ( unsigned int j = 0; j < seg->get_sections_num(); ++j ) { ++ Elf_Half index = seg->get_section_index_at( j ); ++ ++ section* sec = sections[index]; ++ ++ // The NULL section is always generated ++ if ( SHT_NULL == sec->get_type() ) { ++ section_generated[index] = true; ++ continue; ++ } ++ ++ Elf_Xword secAlign = 0; ++ // Fix up the alignment ++ if ( !section_generated[index] && ++ sec->is_address_initialized() && ++ SHT_NOBITS != sec->get_type() && ++ SHT_NULL != sec->get_type() && 0 != sec->get_size() ) { ++ // Align the sections based on the virtual addresses ++ // when possible (this is what matters for execution) ++ Elf64_Off req_offset = ++ sec->get_address() - seg->get_virtual_address(); ++ Elf64_Off cur_offset = current_file_pos - seg_start_pos; ++ if ( req_offset < cur_offset ) { ++ // something has gone awfully wrong, abort! ++ // secAlign would turn out negative, seeking backwards and overwriting previous data ++ return false; ++ } ++ secAlign = req_offset - cur_offset; ++ } ++ else if ( !section_generated[index] && ++ !sec->is_address_initialized() ) { ++ // If no address has been specified then only the section ++ // alignment constraint has to be matched ++ Elf_Xword align = sec->get_addr_align(); ++ if ( align == 0 ) { ++ align = 1; ++ } ++ Elf64_Off error = current_file_pos % align; ++ secAlign = ( align - error ) % align; ++ } ++ else if ( section_generated[index] ) { ++ // Alignment for already generated sections ++ secAlign = ++ sec->get_offset() - seg_start_pos - segment_filesize; ++ } ++ ++ // Determine the segment file and memory sizes ++ // Special case .tbss section (NOBITS) in non TLS segment ++ if ( ( sec->get_flags() & SHF_ALLOC ) && ++ !( ( sec->get_flags() & SHF_TLS ) && ++ ( seg->get_type() != PT_TLS ) && ++ ( SHT_NOBITS == sec->get_type() ) ) ) ++ segment_memory += sec->get_size() + secAlign; ++ ++ if ( SHT_NOBITS != sec->get_type() ) ++ segment_filesize += sec->get_size() + secAlign; ++ ++ // Nothing to be done when generating nested segments ++ if ( section_generated[index] ) { ++ continue; ++ } ++ ++ current_file_pos += secAlign; ++ ++ // Set the section addresses when missing ++ if ( !sec->is_address_initialized() ) ++ sec->set_address( seg->get_virtual_address() + ++ current_file_pos - seg_start_pos ); ++ ++ if ( 0 != sec->get_index() ) ++ sec->set_offset( current_file_pos ); ++ ++ if ( SHT_NOBITS != sec->get_type() ) ++ current_file_pos += sec->get_size(); ++ ++ section_generated[index] = true; ++ } ++ ++ seg->set_file_size( segment_filesize ); ++ ++ // If we already have a memory size from loading an elf file (value > 0), ++ // it must not shrink! ++ // Memory size may be bigger than file size and it is the loader's job to do something ++ // with the surplus bytes in memory, like initializing them with a defined value. ++ if ( seg->get_memory_size() < segment_memory ) { ++ seg->set_memory_size( segment_memory ); ++ } ++ ++ seg->set_offset( seg_start_pos ); ++ } ++ ++ return true; ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool layout_section_table() ++ { ++ // Simply place the section table at the end for now ++ Elf64_Off alignmentError = current_file_pos % 4; ++ current_file_pos += ( 4 - alignmentError ) % 4; ++ header->set_sections_offset( current_file_pos ); ++ return true; ++ } ++ ++ //------------------------------------------------------------------------------ ++ public: ++ friend class Sections; ++ class Sections ++ { ++ public: ++ //------------------------------------------------------------------------------ ++ Sections( elfio* parent_ ) : parent( parent_ ) {} ++ ++ //------------------------------------------------------------------------------ ++ Elf_Half size() const { return (Elf_Half)parent->sections_.size(); } ++ ++ //------------------------------------------------------------------------------ ++ section* operator[]( unsigned int index ) const ++ { ++ section* sec = 0; ++ ++ if ( index < parent->sections_.size() ) { ++ sec = parent->sections_[index]; ++ } ++ ++ return sec; ++ } ++ ++ //------------------------------------------------------------------------------ ++ section* operator[]( const std::string& name ) const ++ { ++ section* sec = 0; ++ ++ std::vector<section*>::const_iterator it; ++ for ( it = parent->sections_.begin(); it != parent->sections_.end(); ++ ++it ) { ++ if ( ( *it )->get_name() == name ) { ++ sec = *it; ++ break; ++ } ++ } ++ ++ return sec; ++ } ++ ++ //------------------------------------------------------------------------------ ++ section* add( const std::string& name ) ++ { ++ section* new_section = parent->create_section(); ++ new_section->set_name( name ); ++ ++ Elf_Half str_index = parent->get_section_name_str_index(); ++ section* string_table( parent->sections_[str_index] ); ++ string_section_accessor str_writer( string_table ); ++ Elf_Word pos = str_writer.add_string( name ); ++ new_section->set_name_string_offset( pos ); ++ ++ return new_section; ++ } ++ ++ //------------------------------------------------------------------------------ ++ std::vector<section*>::iterator begin() ++ { ++ return parent->sections_.begin(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ std::vector<section*>::iterator end() ++ { ++ return parent->sections_.end(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ std::vector<section*>::const_iterator begin() const ++ { ++ return parent->sections_.cbegin(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ std::vector<section*>::const_iterator end() const ++ { ++ return parent->sections_.cend(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ elfio* parent; ++ } sections; ++ ++ //------------------------------------------------------------------------------ ++ public: ++ friend class Segments; ++ class Segments ++ { ++ public: ++ //------------------------------------------------------------------------------ ++ Segments( elfio* parent_ ) : parent( parent_ ) {} ++ ++ //------------------------------------------------------------------------------ ++ Elf_Half size() const { return (Elf_Half)parent->segments_.size(); } ++ ++ //------------------------------------------------------------------------------ ++ segment* operator[]( unsigned int index ) const ++ { ++ return parent->segments_[index]; ++ } ++ ++ //------------------------------------------------------------------------------ ++ segment* add() { return parent->create_segment(); } ++ ++ //------------------------------------------------------------------------------ ++ std::vector<segment*>::iterator begin() ++ { ++ return parent->segments_.begin(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ std::vector<segment*>::iterator end() ++ { ++ return parent->segments_.end(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ std::vector<segment*>::const_iterator begin() const ++ { ++ return parent->segments_.cbegin(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ std::vector<segment*>::const_iterator end() const ++ { ++ return parent->segments_.cend(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ elfio* parent; ++ } segments; ++ ++ //------------------------------------------------------------------------------ ++ private: ++ elf_header* header; ++ std::vector<section*> sections_; ++ std::vector<segment*> segments_; ++ endianess_convertor convertor; ++ ++ Elf_Xword current_file_pos; ++}; ++ ++} // namespace ELFIO ++ ++ ++/*** Start of inlined file: elfio_symbols.hpp ***/ ++#ifndef ELFIO_SYMBOLS_HPP ++#define ELFIO_SYMBOLS_HPP ++ ++namespace ELFIO { ++ ++//------------------------------------------------------------------------------ ++template <class S> class symbol_section_accessor_template ++{ ++ public: ++ //------------------------------------------------------------------------------ ++ symbol_section_accessor_template( const elfio& elf_file_, ++ S* symbol_section_ ) ++ : elf_file( elf_file_ ), symbol_section( symbol_section_ ) ++ { ++ find_hash_section(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Xword get_symbols_num() const ++ { ++ Elf_Xword nRet = 0; ++ if ( 0 != symbol_section->get_entry_size() ) { ++ nRet = ++ symbol_section->get_size() / symbol_section->get_entry_size(); ++ } ++ ++ return nRet; ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool get_symbol( Elf_Xword index, ++ std::string& name, ++ Elf64_Addr& value, ++ Elf_Xword& size, ++ unsigned char& bind, ++ unsigned char& type, ++ Elf_Half& section_index, ++ unsigned char& other ) const ++ { ++ bool ret = false; ++ ++ if ( elf_file.get_class() == ELFCLASS32 ) { ++ ret = generic_get_symbol<Elf32_Sym>( index, name, value, size, bind, ++ type, section_index, other ); ++ } ++ else { ++ ret = generic_get_symbol<Elf64_Sym>( index, name, value, size, bind, ++ type, section_index, other ); ++ } ++ ++ return ret; ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool get_symbol( const std::string& name, ++ Elf64_Addr& value, ++ Elf_Xword& size, ++ unsigned char& bind, ++ unsigned char& type, ++ Elf_Half& section_index, ++ unsigned char& other ) const ++ { ++ bool ret = false; ++ ++ if ( 0 != get_hash_table_index() ) { ++ Elf_Word nbucket = *(const Elf_Word*)hash_section->get_data(); ++ Elf_Word nchain = *(const Elf_Word*)( hash_section->get_data() + ++ sizeof( Elf_Word ) ); ++ Elf_Word val = elf_hash( (const unsigned char*)name.c_str() ); ++ Elf_Word y = *(const Elf_Word*)( hash_section->get_data() + ++ ( 2 + val % nbucket ) * ++ sizeof( Elf_Word ) ); ++ std::string str; ++ get_symbol( y, str, value, size, bind, type, section_index, other ); ++ while ( str != name && STN_UNDEF != y && y < nchain ) { ++ y = *(const Elf_Word*)( hash_section->get_data() + ++ ( 2 + nbucket + y ) * ++ sizeof( Elf_Word ) ); ++ get_symbol( y, str, value, size, bind, type, section_index, ++ other ); ++ } ++ if ( str == name ) { ++ ret = true; ++ } ++ } ++ else { ++ for ( Elf_Xword i = 0; i < get_symbols_num() && !ret; i++ ) { ++ std::string symbol_name; ++ if ( get_symbol( i, symbol_name, value, size, bind, type, ++ section_index, other ) ) { ++ if ( symbol_name == name ) { ++ ret = true; ++ } ++ } ++ } ++ } ++ ++ return ret; ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool get_symbol( const Elf64_Addr& value, ++ std::string& name, ++ Elf_Xword& size, ++ unsigned char& bind, ++ unsigned char& type, ++ Elf_Half& section_index, ++ unsigned char& other ) const ++ { ++ ++ const endianess_convertor& convertor = elf_file.get_convertor(); ++ ++ Elf_Xword idx = 0; ++ bool match = false; ++ Elf64_Addr v = 0; ++ ++ if ( elf_file.get_class() == ELFCLASS32 ) { ++ match = generic_search_symbols<Elf32_Sym>( ++ [&]( const Elf32_Sym* sym ) { ++ return convertor( sym->st_value ) == value; ++ }, ++ idx ); ++ } ++ else { ++ match = generic_search_symbols<Elf64_Sym>( ++ [&]( const Elf64_Sym* sym ) { ++ return convertor( sym->st_value ) == value; ++ }, ++ idx ); ++ } ++ ++ if ( match ) { ++ return get_symbol( idx, name, v, size, bind, type, section_index, ++ other ); ++ } ++ ++ return false; ++ } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Word add_symbol( Elf_Word name, ++ Elf64_Addr value, ++ Elf_Xword size, ++ unsigned char info, ++ unsigned char other, ++ Elf_Half shndx ) ++ { ++ Elf_Word nRet; ++ ++ if ( symbol_section->get_size() == 0 ) { ++ if ( elf_file.get_class() == ELFCLASS32 ) { ++ nRet = generic_add_symbol<Elf32_Sym>( 0, 0, 0, 0, 0, 0 ); ++ } ++ else { ++ nRet = generic_add_symbol<Elf64_Sym>( 0, 0, 0, 0, 0, 0 ); ++ } ++ } ++ ++ if ( elf_file.get_class() == ELFCLASS32 ) { ++ nRet = generic_add_symbol<Elf32_Sym>( name, value, size, info, ++ other, shndx ); ++ } ++ else { ++ nRet = generic_add_symbol<Elf64_Sym>( name, value, size, info, ++ other, shndx ); ++ } ++ ++ return nRet; ++ } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Word add_symbol( Elf_Word name, ++ Elf64_Addr value, ++ Elf_Xword size, ++ unsigned char bind, ++ unsigned char type, ++ unsigned char other, ++ Elf_Half shndx ) ++ { ++ return add_symbol( name, value, size, ELF_ST_INFO( bind, type ), other, ++ shndx ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Word add_symbol( string_section_accessor& pStrWriter, ++ const char* str, ++ Elf64_Addr value, ++ Elf_Xword size, ++ unsigned char info, ++ unsigned char other, ++ Elf_Half shndx ) ++ { ++ Elf_Word index = pStrWriter.add_string( str ); ++ return add_symbol( index, value, size, info, other, shndx ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Word add_symbol( string_section_accessor& pStrWriter, ++ const char* str, ++ Elf64_Addr value, ++ Elf_Xword size, ++ unsigned char bind, ++ unsigned char type, ++ unsigned char other, ++ Elf_Half shndx ) ++ { ++ return add_symbol( pStrWriter, str, value, size, ++ ELF_ST_INFO( bind, type ), other, shndx ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Xword arrange_local_symbols( ++ std::function<void( Elf_Xword first, Elf_Xword second )> func = ++ nullptr ) ++ { ++ int nRet = 0; ++ ++ if ( elf_file.get_class() == ELFCLASS32 ) { ++ nRet = generic_arrange_local_symbols<Elf32_Sym>( func ); ++ } ++ else { ++ nRet = generic_arrange_local_symbols<Elf64_Sym>( func ); ++ } ++ ++ return nRet; ++ } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ //------------------------------------------------------------------------------ ++ void find_hash_section() ++ { ++ hash_section = 0; ++ hash_section_index = 0; ++ Elf_Half nSecNo = elf_file.sections.size(); ++ for ( Elf_Half i = 0; i < nSecNo && 0 == hash_section_index; ++i ) { ++ const section* sec = elf_file.sections[i]; ++ if ( sec->get_link() == symbol_section->get_index() ) { ++ hash_section = sec; ++ hash_section_index = i; ++ } ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Half get_string_table_index() const ++ { ++ return (Elf_Half)symbol_section->get_link(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Half get_hash_table_index() const { return hash_section_index; } ++ ++ //------------------------------------------------------------------------------ ++ template <class T> const T* generic_get_symbol_ptr( Elf_Xword index ) const ++ { ++ if ( 0 != symbol_section->get_data() && index < get_symbols_num() ) { ++ const T* pSym = reinterpret_cast<const T*>( ++ symbol_section->get_data() + ++ index * symbol_section->get_entry_size() ); ++ ++ return pSym; ++ } ++ ++ return nullptr; ++ } ++ ++ //------------------------------------------------------------------------------ ++ template <class T> ++ bool generic_search_symbols( std::function<bool( const T* )> match, ++ Elf_Xword& idx ) const ++ { ++ for ( Elf_Xword i = 0; i < get_symbols_num(); i++ ) { ++ const T* symPtr = generic_get_symbol_ptr<T>( i ); ++ ++ if ( symPtr == nullptr ) ++ return false; ++ ++ if ( match( symPtr ) ) { ++ idx = i; ++ return true; ++ } ++ } ++ ++ return false; ++ } ++ ++ //------------------------------------------------------------------------------ ++ template <class T> ++ bool generic_get_symbol( Elf_Xword index, ++ std::string& name, ++ Elf64_Addr& value, ++ Elf_Xword& size, ++ unsigned char& bind, ++ unsigned char& type, ++ Elf_Half& section_index, ++ unsigned char& other ) const ++ { ++ bool ret = false; ++ ++ if ( 0 != symbol_section->get_data() && index < get_symbols_num() ) { ++ const T* pSym = reinterpret_cast<const T*>( ++ symbol_section->get_data() + ++ index * symbol_section->get_entry_size() ); ++ ++ const endianess_convertor& convertor = elf_file.get_convertor(); ++ ++ section* string_section = ++ elf_file.sections[get_string_table_index()]; ++ string_section_accessor str_reader( string_section ); ++ const char* pStr = ++ str_reader.get_string( convertor( pSym->st_name ) ); ++ if ( 0 != pStr ) { ++ name = pStr; ++ } ++ value = convertor( pSym->st_value ); ++ size = convertor( pSym->st_size ); ++ bind = ELF_ST_BIND( pSym->st_info ); ++ type = ELF_ST_TYPE( pSym->st_info ); ++ section_index = convertor( pSym->st_shndx ); ++ other = pSym->st_other; ++ ++ ret = true; ++ } ++ ++ return ret; ++ } ++ ++ //------------------------------------------------------------------------------ ++ template <class T> ++ Elf_Word generic_add_symbol( Elf_Word name, ++ Elf64_Addr value, ++ Elf_Xword size, ++ unsigned char info, ++ unsigned char other, ++ Elf_Half shndx ) ++ { ++ const endianess_convertor& convertor = elf_file.get_convertor(); ++ ++ T entry; ++ entry.st_name = convertor( name ); ++ entry.st_value = value; ++ entry.st_value = convertor( entry.st_value ); ++ entry.st_size = size; ++ entry.st_size = convertor( entry.st_size ); ++ entry.st_info = convertor( info ); ++ entry.st_other = convertor( other ); ++ entry.st_shndx = convertor( shndx ); ++ ++ symbol_section->append_data( reinterpret_cast<char*>( &entry ), ++ sizeof( entry ) ); ++ ++ Elf_Word nRet = symbol_section->get_size() / sizeof( entry ) - 1; ++ ++ return nRet; ++ } ++ ++ //------------------------------------------------------------------------------ ++ template <class T> ++ Elf_Xword generic_arrange_local_symbols( ++ std::function<void( Elf_Xword first, Elf_Xword second )> func ) ++ { ++ const endianess_convertor& convertor = elf_file.get_convertor(); ++ const Elf_Xword size = symbol_section->get_entry_size(); ++ ++ Elf_Xword first_not_local = ++ 1; // Skip the first entry. It is always NOTYPE ++ Elf_Xword current = 0; ++ Elf_Xword count = get_symbols_num(); ++ ++ while ( true ) { ++ T* p1 = nullptr; ++ T* p2 = nullptr; ++ ++ while ( first_not_local < count ) { ++ p1 = const_cast<T*>( ++ generic_get_symbol_ptr<T>( first_not_local ) ); ++ if ( ELF_ST_BIND( convertor( p1->st_info ) ) != STB_LOCAL ) ++ break; ++ ++first_not_local; ++ } ++ ++ current = first_not_local + 1; ++ while ( current < count ) { ++ p2 = const_cast<T*>( generic_get_symbol_ptr<T>( current ) ); ++ if ( ELF_ST_BIND( convertor( p2->st_info ) ) == STB_LOCAL ) ++ break; ++ ++current; ++ } ++ ++ if ( first_not_local < count && current < count ) { ++ if ( func ) ++ func( first_not_local, current ); ++ ++ // Swap the symbols ++ T tmp; ++ std::copy( p1, p1 + 1, &tmp ); ++ std::copy( p2, p2 + 1, p1 ); ++ std::copy( &tmp, &tmp + 1, p2 ); ++ } ++ else { ++ // Update 'info' field of the section ++ symbol_section->set_info( first_not_local ); ++ break; ++ } ++ } ++ ++ // Elf_Word nRet = symbol_section->get_size() / sizeof(entry) - 1; ++ ++ return first_not_local; ++ } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ const elfio& elf_file; ++ S* symbol_section; ++ Elf_Half hash_section_index; ++ const section* hash_section; ++}; ++ ++using symbol_section_accessor = symbol_section_accessor_template<section>; ++using const_symbol_section_accessor = ++ symbol_section_accessor_template<const section>; ++ ++} // namespace ELFIO ++ ++#endif // ELFIO_SYMBOLS_HPP ++ ++/*** End of inlined file: elfio_symbols.hpp ***/ ++ ++ ++/*** Start of inlined file: elfio_note.hpp ***/ ++#ifndef ELFIO_NOTE_HPP ++#define ELFIO_NOTE_HPP ++ ++namespace ELFIO { ++ ++//------------------------------------------------------------------------------ ++// There are discrepancies in documentations. SCO documentation ++// (http://www.sco.com/developers/gabi/latest/ch5.pheader.html#note_section) ++// requires 8 byte entries alignment for 64-bit ELF file, ++// but Oracle's definition uses the same structure ++// for 32-bit and 64-bit formats. ++// (https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-18048.html) ++// ++// It looks like EM_X86_64 Linux implementation is similar to Oracle's ++// definition. Therefore, the same alignment works for both formats ++//------------------------------------------------------------------------------ ++ ++//------------------------------------------------------------------------------ ++template <class S> class note_section_accessor_template ++{ ++ public: ++ //------------------------------------------------------------------------------ ++ note_section_accessor_template( const elfio& elf_file_, S* section_ ) ++ : elf_file( elf_file_ ), note_section( section_ ) ++ { ++ process_section(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Word get_notes_num() const ++ { ++ return (Elf_Word)note_start_positions.size(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool get_note( Elf_Word index, ++ Elf_Word& type, ++ std::string& name, ++ void*& desc, ++ Elf_Word& descSize ) const ++ { ++ if ( index >= note_section->get_size() ) { ++ return false; ++ } ++ ++ const char* pData = ++ note_section->get_data() + note_start_positions[index]; ++ int align = sizeof( Elf_Word ); ++ ++ const endianess_convertor& convertor = elf_file.get_convertor(); ++ type = convertor( *(const Elf_Word*)( pData + 2 * align ) ); ++ Elf_Word namesz = convertor( *(const Elf_Word*)( pData ) ); ++ descSize = convertor( *(const Elf_Word*)( pData + sizeof( namesz ) ) ); ++ ++ Elf_Xword max_name_size = ++ note_section->get_size() - note_start_positions[index]; ++ if ( namesz < 1 || namesz > max_name_size || ++ (Elf_Xword)namesz + descSize > max_name_size ) { ++ return false; ++ } ++ name.assign( pData + 3 * align, namesz - 1 ); ++ if ( 0 == descSize ) { ++ desc = 0; ++ } ++ else { ++ desc = ++ const_cast<char*>( pData + 3 * align + ++ ( ( namesz + align - 1 ) / align ) * align ); ++ } ++ ++ return true; ++ } ++ ++ //------------------------------------------------------------------------------ ++ void add_note( Elf_Word type, ++ const std::string& name, ++ const void* desc, ++ Elf_Word descSize ) ++ { ++ const endianess_convertor& convertor = elf_file.get_convertor(); ++ ++ int align = sizeof( Elf_Word ); ++ Elf_Word nameLen = (Elf_Word)name.size() + 1; ++ Elf_Word nameLenConv = convertor( nameLen ); ++ std::string buffer( reinterpret_cast<char*>( &nameLenConv ), align ); ++ Elf_Word descSizeConv = convertor( descSize ); ++ ++ buffer.append( reinterpret_cast<char*>( &descSizeConv ), align ); ++ type = convertor( type ); ++ buffer.append( reinterpret_cast<char*>( &type ), align ); ++ buffer.append( name ); ++ buffer.append( 1, '\x00' ); ++ const char pad[] = { '\0', '\0', '\0', '\0' }; ++ if ( nameLen % align != 0 ) { ++ buffer.append( pad, align - nameLen % align ); ++ } ++ if ( desc != 0 && descSize != 0 ) { ++ buffer.append( reinterpret_cast<const char*>( desc ), descSize ); ++ if ( descSize % align != 0 ) { ++ buffer.append( pad, align - descSize % align ); ++ } ++ } ++ ++ note_start_positions.push_back( note_section->get_size() ); ++ note_section->append_data( buffer ); ++ } ++ ++ private: ++ //------------------------------------------------------------------------------ ++ void process_section() ++ { ++ const endianess_convertor& convertor = elf_file.get_convertor(); ++ const char* data = note_section->get_data(); ++ Elf_Xword size = note_section->get_size(); ++ Elf_Xword current = 0; ++ ++ note_start_positions.clear(); ++ ++ // Is it empty? ++ if ( 0 == data || 0 == size ) { ++ return; ++ } ++ ++ Elf_Word align = sizeof( Elf_Word ); ++ while ( current + (Elf_Xword)3 * align <= size ) { ++ note_start_positions.push_back( current ); ++ Elf_Word namesz = convertor( *(const Elf_Word*)( data + current ) ); ++ Elf_Word descsz = convertor( ++ *(const Elf_Word*)( data + current + sizeof( namesz ) ) ); ++ ++ current += (Elf_Xword)3 * sizeof( Elf_Word ) + ++ ( ( namesz + align - 1 ) / align ) * (Elf_Xword)align + ++ ( ( descsz + align - 1 ) / align ) * (Elf_Xword)align; ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ const elfio& elf_file; ++ S* note_section; ++ std::vector<Elf_Xword> note_start_positions; ++}; ++ ++using note_section_accessor = note_section_accessor_template<section>; ++using const_note_section_accessor = ++ note_section_accessor_template<const section>; ++ ++} // namespace ELFIO ++ ++#endif // ELFIO_NOTE_HPP ++ ++/*** End of inlined file: elfio_note.hpp ***/ ++ ++ ++/*** Start of inlined file: elfio_relocation.hpp ***/ ++#ifndef ELFIO_RELOCATION_HPP ++#define ELFIO_RELOCATION_HPP ++ ++namespace ELFIO { ++ ++template <typename T> struct get_sym_and_type; ++template <> struct get_sym_and_type<Elf32_Rel> ++{ ++ static int get_r_sym( Elf_Xword info ) ++ { ++ return ELF32_R_SYM( (Elf_Word)info ); ++ } ++ static int get_r_type( Elf_Xword info ) ++ { ++ return ELF32_R_TYPE( (Elf_Word)info ); ++ } ++}; ++template <> struct get_sym_and_type<Elf32_Rela> ++{ ++ static int get_r_sym( Elf_Xword info ) ++ { ++ return ELF32_R_SYM( (Elf_Word)info ); ++ } ++ static int get_r_type( Elf_Xword info ) ++ { ++ return ELF32_R_TYPE( (Elf_Word)info ); ++ } ++}; ++template <> struct get_sym_and_type<Elf64_Rel> ++{ ++ static int get_r_sym( Elf_Xword info ) { return ELF64_R_SYM( info ); } ++ static int get_r_type( Elf_Xword info ) { return ELF64_R_TYPE( info ); } ++}; ++template <> struct get_sym_and_type<Elf64_Rela> ++{ ++ static int get_r_sym( Elf_Xword info ) { return ELF64_R_SYM( info ); } ++ static int get_r_type( Elf_Xword info ) { return ELF64_R_TYPE( info ); } ++}; ++ ++//------------------------------------------------------------------------------ ++template <class S> class relocation_section_accessor_template ++{ ++ public: ++ //------------------------------------------------------------------------------ ++ relocation_section_accessor_template( const elfio& elf_file_, S* section_ ) ++ : elf_file( elf_file_ ), relocation_section( section_ ) ++ { ++ } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Xword get_entries_num() const ++ { ++ Elf_Xword nRet = 0; ++ ++ if ( 0 != relocation_section->get_entry_size() ) { ++ nRet = relocation_section->get_size() / ++ relocation_section->get_entry_size(); ++ } ++ ++ return nRet; ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool get_entry( Elf_Xword index, ++ Elf64_Addr& offset, ++ Elf_Word& symbol, ++ Elf_Word& type, ++ Elf_Sxword& addend ) const ++ { ++ if ( index >= get_entries_num() ) { // Is index valid ++ return false; ++ } ++ ++ if ( elf_file.get_class() == ELFCLASS32 ) { ++ if ( SHT_REL == relocation_section->get_type() ) { ++ generic_get_entry_rel<Elf32_Rel>( index, offset, symbol, type, ++ addend ); ++ } ++ else if ( SHT_RELA == relocation_section->get_type() ) { ++ generic_get_entry_rela<Elf32_Rela>( index, offset, symbol, type, ++ addend ); ++ } ++ } ++ else { ++ if ( SHT_REL == relocation_section->get_type() ) { ++ generic_get_entry_rel<Elf64_Rel>( index, offset, symbol, type, ++ addend ); ++ } ++ else if ( SHT_RELA == relocation_section->get_type() ) { ++ generic_get_entry_rela<Elf64_Rela>( index, offset, symbol, type, ++ addend ); ++ } ++ } ++ ++ return true; ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool get_entry( Elf_Xword index, ++ Elf64_Addr& offset, ++ Elf64_Addr& symbolValue, ++ std::string& symbolName, ++ Elf_Word& type, ++ Elf_Sxword& addend, ++ Elf_Sxword& calcValue ) const ++ { ++ // Do regular job ++ Elf_Word symbol; ++ bool ret = get_entry( index, offset, symbol, type, addend ); ++ ++ // Find the symbol ++ Elf_Xword size; ++ unsigned char bind; ++ unsigned char symbolType; ++ Elf_Half section; ++ unsigned char other; ++ ++ symbol_section_accessor symbols( ++ elf_file, elf_file.sections[get_symbol_table_index()] ); ++ ret = ret && symbols.get_symbol( symbol, symbolName, symbolValue, size, ++ bind, symbolType, section, other ); ++ ++ if ( ret ) { // Was it successful? ++ switch ( type ) { ++ case R_386_NONE: // none ++ calcValue = 0; ++ break; ++ case R_386_32: // S + A ++ calcValue = symbolValue + addend; ++ break; ++ case R_386_PC32: // S + A - P ++ calcValue = symbolValue + addend - offset; ++ break; ++ case R_386_GOT32: // G + A - P ++ calcValue = 0; ++ break; ++ case R_386_PLT32: // L + A - P ++ calcValue = 0; ++ break; ++ case R_386_COPY: // none ++ calcValue = 0; ++ break; ++ case R_386_GLOB_DAT: // S ++ case R_386_JMP_SLOT: // S ++ calcValue = symbolValue; ++ break; ++ case R_386_RELATIVE: // B + A ++ calcValue = addend; ++ break; ++ case R_386_GOTOFF: // S + A - GOT ++ calcValue = 0; ++ break; ++ case R_386_GOTPC: // GOT + A - P ++ calcValue = 0; ++ break; ++ default: // Not recognized symbol! ++ calcValue = 0; ++ break; ++ } ++ } ++ ++ return ret; ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool set_entry( Elf_Xword index, ++ Elf64_Addr offset, ++ Elf_Word symbol, ++ Elf_Word type, ++ Elf_Sxword addend ) ++ { ++ if ( index >= get_entries_num() ) { // Is index valid ++ return false; ++ } ++ ++ if ( elf_file.get_class() == ELFCLASS32 ) { ++ if ( SHT_REL == relocation_section->get_type() ) { ++ generic_set_entry_rel<Elf32_Rel>( index, offset, symbol, type, ++ addend ); ++ } ++ else if ( SHT_RELA == relocation_section->get_type() ) { ++ generic_set_entry_rela<Elf32_Rela>( index, offset, symbol, type, ++ addend ); ++ } ++ } ++ else { ++ if ( SHT_REL == relocation_section->get_type() ) { ++ generic_set_entry_rel<Elf64_Rel>( index, offset, symbol, type, ++ addend ); ++ } ++ else if ( SHT_RELA == relocation_section->get_type() ) { ++ generic_set_entry_rela<Elf64_Rela>( index, offset, symbol, type, ++ addend ); ++ } ++ } ++ ++ return true; ++ } ++ ++ //------------------------------------------------------------------------------ ++ void add_entry( Elf64_Addr offset, Elf_Xword info ) ++ { ++ if ( elf_file.get_class() == ELFCLASS32 ) { ++ generic_add_entry<Elf32_Rel>( offset, info ); ++ } ++ else { ++ generic_add_entry<Elf64_Rel>( offset, info ); ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ void add_entry( Elf64_Addr offset, Elf_Word symbol, unsigned char type ) ++ { ++ Elf_Xword info; ++ if ( elf_file.get_class() == ELFCLASS32 ) { ++ info = ELF32_R_INFO( (Elf_Xword)symbol, type ); ++ } ++ else { ++ info = ELF64_R_INFO( (Elf_Xword)symbol, type ); ++ } ++ ++ add_entry( offset, info ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ void add_entry( Elf64_Addr offset, Elf_Xword info, Elf_Sxword addend ) ++ { ++ if ( elf_file.get_class() == ELFCLASS32 ) { ++ generic_add_entry<Elf32_Rela>( offset, info, addend ); ++ } ++ else { ++ generic_add_entry<Elf64_Rela>( offset, info, addend ); ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ void add_entry( Elf64_Addr offset, ++ Elf_Word symbol, ++ unsigned char type, ++ Elf_Sxword addend ) ++ { ++ Elf_Xword info; ++ if ( elf_file.get_class() == ELFCLASS32 ) { ++ info = ELF32_R_INFO( (Elf_Xword)symbol, type ); ++ } ++ else { ++ info = ELF64_R_INFO( (Elf_Xword)symbol, type ); ++ } ++ ++ add_entry( offset, info, addend ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ void add_entry( string_section_accessor str_writer, ++ const char* str, ++ symbol_section_accessor sym_writer, ++ Elf64_Addr value, ++ Elf_Word size, ++ unsigned char sym_info, ++ unsigned char other, ++ Elf_Half shndx, ++ Elf64_Addr offset, ++ unsigned char type ) ++ { ++ Elf_Word str_index = str_writer.add_string( str ); ++ Elf_Word sym_index = sym_writer.add_symbol( str_index, value, size, ++ sym_info, other, shndx ); ++ add_entry( offset, sym_index, type ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ void swap_symbols( Elf_Xword first, Elf_Xword second ) ++ { ++ Elf64_Addr offset; ++ Elf_Word symbol; ++ Elf_Word rtype; ++ Elf_Sxword addend; ++ for ( Elf_Word i = 0; i < get_entries_num(); i++ ) { ++ get_entry( i, offset, symbol, rtype, addend ); ++ if ( symbol == first ) { ++ set_entry( i, offset, (Elf_Word)second, rtype, addend ); ++ } ++ if ( symbol == second ) { ++ set_entry( i, offset, (Elf_Word)first, rtype, addend ); ++ } ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ //------------------------------------------------------------------------------ ++ Elf_Half get_symbol_table_index() const ++ { ++ return (Elf_Half)relocation_section->get_link(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ template <class T> ++ void generic_get_entry_rel( Elf_Xword index, ++ Elf64_Addr& offset, ++ Elf_Word& symbol, ++ Elf_Word& type, ++ Elf_Sxword& addend ) const ++ { ++ const endianess_convertor& convertor = elf_file.get_convertor(); ++ ++ const T* pEntry = reinterpret_cast<const T*>( ++ relocation_section->get_data() + ++ index * relocation_section->get_entry_size() ); ++ offset = convertor( pEntry->r_offset ); ++ Elf_Xword tmp = convertor( pEntry->r_info ); ++ symbol = get_sym_and_type<T>::get_r_sym( tmp ); ++ type = get_sym_and_type<T>::get_r_type( tmp ); ++ addend = 0; ++ } ++ ++ //------------------------------------------------------------------------------ ++ template <class T> ++ void generic_get_entry_rela( Elf_Xword index, ++ Elf64_Addr& offset, ++ Elf_Word& symbol, ++ Elf_Word& type, ++ Elf_Sxword& addend ) const ++ { ++ const endianess_convertor& convertor = elf_file.get_convertor(); ++ ++ const T* pEntry = reinterpret_cast<const T*>( ++ relocation_section->get_data() + ++ index * relocation_section->get_entry_size() ); ++ offset = convertor( pEntry->r_offset ); ++ Elf_Xword tmp = convertor( pEntry->r_info ); ++ symbol = get_sym_and_type<T>::get_r_sym( tmp ); ++ type = get_sym_and_type<T>::get_r_type( tmp ); ++ addend = convertor( pEntry->r_addend ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ template <class T> ++ void generic_set_entry_rel( Elf_Xword index, ++ Elf64_Addr offset, ++ Elf_Word symbol, ++ Elf_Word type, ++ Elf_Sxword ) ++ { ++ const endianess_convertor& convertor = elf_file.get_convertor(); ++ ++ T* pEntry = const_cast<T*>( reinterpret_cast<const T*>( ++ relocation_section->get_data() + ++ index * relocation_section->get_entry_size() ) ); ++ ++ if ( elf_file.get_class() == ELFCLASS32 ) { ++ pEntry->r_info = ELF32_R_INFO( (Elf_Xword)symbol, type ); ++ } ++ else { ++ pEntry->r_info = ELF64_R_INFO( (Elf_Xword)symbol, type ); ++ } ++ pEntry->r_offset = offset; ++ pEntry->r_offset = convertor( pEntry->r_offset ); ++ pEntry->r_info = convertor( pEntry->r_info ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ template <class T> ++ void generic_set_entry_rela( Elf_Xword index, ++ Elf64_Addr offset, ++ Elf_Word symbol, ++ Elf_Word type, ++ Elf_Sxword addend ) ++ { ++ const endianess_convertor& convertor = elf_file.get_convertor(); ++ ++ T* pEntry = const_cast<T*>( reinterpret_cast<const T*>( ++ relocation_section->get_data() + ++ index * relocation_section->get_entry_size() ) ); ++ ++ if ( elf_file.get_class() == ELFCLASS32 ) { ++ pEntry->r_info = ELF32_R_INFO( (Elf_Xword)symbol, type ); ++ } ++ else { ++ pEntry->r_info = ELF64_R_INFO( (Elf_Xword)symbol, type ); ++ } ++ pEntry->r_offset = offset; ++ pEntry->r_addend = addend; ++ pEntry->r_offset = convertor( pEntry->r_offset ); ++ pEntry->r_info = convertor( pEntry->r_info ); ++ pEntry->r_addend = convertor( pEntry->r_addend ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ template <class T> ++ void generic_add_entry( Elf64_Addr offset, Elf_Xword info ) ++ { ++ const endianess_convertor& convertor = elf_file.get_convertor(); ++ ++ T entry; ++ entry.r_offset = offset; ++ entry.r_info = info; ++ entry.r_offset = convertor( entry.r_offset ); ++ entry.r_info = convertor( entry.r_info ); ++ ++ relocation_section->append_data( reinterpret_cast<char*>( &entry ), ++ sizeof( entry ) ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ template <class T> ++ void ++ generic_add_entry( Elf64_Addr offset, Elf_Xword info, Elf_Sxword addend ) ++ { ++ const endianess_convertor& convertor = elf_file.get_convertor(); ++ ++ T entry; ++ entry.r_offset = offset; ++ entry.r_info = info; ++ entry.r_addend = addend; ++ entry.r_offset = convertor( entry.r_offset ); ++ entry.r_info = convertor( entry.r_info ); ++ entry.r_addend = convertor( entry.r_addend ); ++ ++ relocation_section->append_data( reinterpret_cast<char*>( &entry ), ++ sizeof( entry ) ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ const elfio& elf_file; ++ S* relocation_section; ++}; ++ ++using relocation_section_accessor = ++ relocation_section_accessor_template<section>; ++using const_relocation_section_accessor = ++ relocation_section_accessor_template<const section>; ++ ++} // namespace ELFIO ++ ++#endif // ELFIO_RELOCATION_HPP ++ ++/*** End of inlined file: elfio_relocation.hpp ***/ ++ ++ ++/*** Start of inlined file: elfio_dynamic.hpp ***/ ++#ifndef ELFIO_DYNAMIC_HPP ++#define ELFIO_DYNAMIC_HPP ++ ++namespace ELFIO { ++ ++//------------------------------------------------------------------------------ ++template <class S> class dynamic_section_accessor_template ++{ ++ public: ++ //------------------------------------------------------------------------------ ++ dynamic_section_accessor_template( const elfio& elf_file_, S* section_ ) ++ : elf_file( elf_file_ ), dynamic_section( section_ ) ++ { ++ } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Xword get_entries_num() const ++ { ++ Elf_Xword nRet = 0; ++ ++ if ( 0 != dynamic_section->get_entry_size() ) { ++ nRet = ++ dynamic_section->get_size() / dynamic_section->get_entry_size(); ++ } ++ ++ return nRet; ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool get_entry( Elf_Xword index, ++ Elf_Xword& tag, ++ Elf_Xword& value, ++ std::string& str ) const ++ { ++ if ( index >= get_entries_num() ) { // Is index valid ++ return false; ++ } ++ ++ if ( elf_file.get_class() == ELFCLASS32 ) { ++ generic_get_entry_dyn<Elf32_Dyn>( index, tag, value ); ++ } ++ else { ++ generic_get_entry_dyn<Elf64_Dyn>( index, tag, value ); ++ } ++ ++ // If the tag may have a string table reference, prepare the string ++ if ( tag == DT_NEEDED || tag == DT_SONAME || tag == DT_RPATH || ++ tag == DT_RUNPATH ) { ++ string_section_accessor strsec = ++ elf_file.sections[get_string_table_index()]; ++ const char* result = strsec.get_string( value ); ++ if ( 0 == result ) { ++ str.clear(); ++ return false; ++ } ++ str = result; ++ } ++ else { ++ str.clear(); ++ } ++ ++ return true; ++ } ++ ++ //------------------------------------------------------------------------------ ++ void add_entry( Elf_Xword tag, Elf_Xword value ) ++ { ++ if ( elf_file.get_class() == ELFCLASS32 ) { ++ generic_add_entry<Elf32_Dyn>( tag, value ); ++ } ++ else { ++ generic_add_entry<Elf64_Dyn>( tag, value ); ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ void add_entry( Elf_Xword tag, const std::string& str ) ++ { ++ string_section_accessor strsec = ++ elf_file.sections[get_string_table_index()]; ++ Elf_Xword value = strsec.add_string( str ); ++ add_entry( tag, value ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ //------------------------------------------------------------------------------ ++ Elf_Half get_string_table_index() const ++ { ++ return (Elf_Half)dynamic_section->get_link(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ template <class T> ++ void generic_get_entry_dyn( Elf_Xword index, ++ Elf_Xword& tag, ++ Elf_Xword& value ) const ++ { ++ const endianess_convertor& convertor = elf_file.get_convertor(); ++ ++ // Check unusual case when dynamic section has no data ++ if ( dynamic_section->get_data() == 0 || ++ ( index + 1 ) * dynamic_section->get_entry_size() > ++ dynamic_section->get_size() ) { ++ tag = DT_NULL; ++ value = 0; ++ return; ++ } ++ ++ const T* pEntry = reinterpret_cast<const T*>( ++ dynamic_section->get_data() + ++ index * dynamic_section->get_entry_size() ); ++ tag = convertor( pEntry->d_tag ); ++ switch ( tag ) { ++ case DT_NULL: ++ case DT_SYMBOLIC: ++ case DT_TEXTREL: ++ case DT_BIND_NOW: ++ value = 0; ++ break; ++ case DT_NEEDED: ++ case DT_PLTRELSZ: ++ case DT_RELASZ: ++ case DT_RELAENT: ++ case DT_STRSZ: ++ case DT_SYMENT: ++ case DT_SONAME: ++ case DT_RPATH: ++ case DT_RELSZ: ++ case DT_RELENT: ++ case DT_PLTREL: ++ case DT_INIT_ARRAYSZ: ++ case DT_FINI_ARRAYSZ: ++ case DT_RUNPATH: ++ case DT_FLAGS: ++ case DT_PREINIT_ARRAYSZ: ++ value = convertor( pEntry->d_un.d_val ); ++ break; ++ case DT_PLTGOT: ++ case DT_HASH: ++ case DT_STRTAB: ++ case DT_SYMTAB: ++ case DT_RELA: ++ case DT_INIT: ++ case DT_FINI: ++ case DT_REL: ++ case DT_DEBUG: ++ case DT_JMPREL: ++ case DT_INIT_ARRAY: ++ case DT_FINI_ARRAY: ++ case DT_PREINIT_ARRAY: ++ default: ++ value = convertor( pEntry->d_un.d_ptr ); ++ break; ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ template <class T> void generic_add_entry( Elf_Xword tag, Elf_Xword value ) ++ { ++ const endianess_convertor& convertor = elf_file.get_convertor(); ++ ++ T entry; ++ ++ switch ( tag ) { ++ case DT_NULL: ++ case DT_SYMBOLIC: ++ case DT_TEXTREL: ++ case DT_BIND_NOW: ++ value = 0; ++ case DT_NEEDED: ++ case DT_PLTRELSZ: ++ case DT_RELASZ: ++ case DT_RELAENT: ++ case DT_STRSZ: ++ case DT_SYMENT: ++ case DT_SONAME: ++ case DT_RPATH: ++ case DT_RELSZ: ++ case DT_RELENT: ++ case DT_PLTREL: ++ case DT_INIT_ARRAYSZ: ++ case DT_FINI_ARRAYSZ: ++ case DT_RUNPATH: ++ case DT_FLAGS: ++ case DT_PREINIT_ARRAYSZ: ++ entry.d_un.d_val = convertor( value ); ++ break; ++ case DT_PLTGOT: ++ case DT_HASH: ++ case DT_STRTAB: ++ case DT_SYMTAB: ++ case DT_RELA: ++ case DT_INIT: ++ case DT_FINI: ++ case DT_REL: ++ case DT_DEBUG: ++ case DT_JMPREL: ++ case DT_INIT_ARRAY: ++ case DT_FINI_ARRAY: ++ case DT_PREINIT_ARRAY: ++ default: ++ entry.d_un.d_ptr = convertor( value ); ++ break; ++ } ++ ++ entry.d_tag = convertor( tag ); ++ ++ dynamic_section->append_data( reinterpret_cast<char*>( &entry ), ++ sizeof( entry ) ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ const elfio& elf_file; ++ S* dynamic_section; ++}; ++ ++using dynamic_section_accessor = dynamic_section_accessor_template<section>; ++using const_dynamic_section_accessor = ++ dynamic_section_accessor_template<const section>; ++ ++} // namespace ELFIO ++ ++#endif // ELFIO_DYNAMIC_HPP ++ ++/*** End of inlined file: elfio_dynamic.hpp ***/ ++ ++ ++/*** Start of inlined file: elfio_modinfo.hpp ***/ ++#ifndef ELFIO_MODINFO_HPP ++#define ELFIO_MODINFO_HPP ++ ++#include <string> ++#include <vector> ++ ++namespace ELFIO { ++ ++//------------------------------------------------------------------------------ ++template <class S> class modinfo_section_accessor_template ++{ ++ public: ++ //------------------------------------------------------------------------------ ++ modinfo_section_accessor_template( S* section_ ) ++ : modinfo_section( section_ ) ++ { ++ process_section(); ++ } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Word get_attribute_num() const { return (Elf_Word)content.size(); } ++ ++ //------------------------------------------------------------------------------ ++ bool ++ get_attribute( Elf_Word no, std::string& field, std::string& value ) const ++ { ++ if ( no < content.size() ) { ++ field = content[no].first; ++ value = content[no].second; ++ return true; ++ } ++ ++ return false; ++ } ++ ++ //------------------------------------------------------------------------------ ++ bool get_attribute( std::string field_name, std::string& value ) const ++ { ++ for ( auto i = content.begin(); i != content.end(); i++ ) { ++ if ( field_name == i->first ) { ++ value = i->second; ++ return true; ++ } ++ } ++ ++ return false; ++ } ++ ++ //------------------------------------------------------------------------------ ++ Elf_Word add_attribute( std::string field, std::string value ) ++ { ++ Elf_Word current_position = 0; ++ ++ if ( modinfo_section ) { ++ // Strings are addeded to the end of the current section data ++ current_position = (Elf_Word)modinfo_section->get_size(); ++ ++ std::string attribute = field + "=" + value; ++ ++ modinfo_section->append_data( attribute + '\0' ); ++ content.push_back( ++ std::pair<std::string, std::string>( field, value ) ); ++ } ++ ++ return current_position; ++ } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ void process_section() ++ { ++ const char* pdata = modinfo_section->get_data(); ++ if ( pdata ) { ++ ELFIO::Elf_Xword i = 0; ++ while ( i < modinfo_section->get_size() ) { ++ while ( i < modinfo_section->get_size() && !pdata[i] ) ++ i++; ++ if ( i < modinfo_section->get_size() ) { ++ std::string info = pdata + i; ++ size_t loc = info.find( '=' ); ++ std::pair<std::string, std::string> attribute( ++ info.substr( 0, loc ), info.substr( loc + 1 ) ); ++ ++ content.push_back( attribute ); ++ ++ i += info.length(); ++ } ++ } ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ private: ++ S* modinfo_section; ++ std::vector<std::pair<std::string, std::string>> content; ++}; ++ ++using modinfo_section_accessor = modinfo_section_accessor_template<section>; ++using const_modinfo_section_accessor = ++ modinfo_section_accessor_template<const section>; ++ ++} // namespace ELFIO ++ ++#endif // ELFIO_MODINFO_HPP ++ ++/*** End of inlined file: elfio_modinfo.hpp ***/ ++ ++#ifdef _MSC_VER ++#pragma warning( pop ) ++#endif ++ ++#endif // ELFIO_HPP ++ ++/*** End of inlined file: elfio.hpp ***/ ++ ++ ++namespace ELFIO { ++ ++static struct class_table_t ++{ ++ const char key; ++ const char* str; ++} class_table[] = { ++ { ELFCLASS32, "ELF32" }, ++ { ELFCLASS64, "ELF64" }, ++}; ++ ++static struct endian_table_t ++{ ++ const char key; ++ const char* str; ++} endian_table[] = { ++ { ELFDATANONE, "None" }, ++ { ELFDATA2LSB, "Little endian" }, ++ { ELFDATA2MSB, "Big endian" }, ++}; ++ ++static struct version_table_t ++{ ++ const Elf64_Word key; ++ const char* str; ++} version_table[] = { ++ { EV_NONE, "None" }, ++ { EV_CURRENT, "Current" }, ++}; ++ ++static struct type_table_t ++{ ++ const Elf32_Half key; ++ const char* str; ++} type_table[] = { ++ { ET_NONE, "No file type" }, { ET_REL, "Relocatable file" }, ++ { ET_EXEC, "Executable file" }, { ET_DYN, "Shared object file" }, ++ { ET_CORE, "Core file" }, ++}; ++ ++static struct machine_table_t ++{ ++ const Elf64_Half key; ++ const char* str; ++} machine_table[] = { ++ { EM_NONE, "No machine" }, ++ { EM_M32, "AT&T WE 32100" }, ++ { EM_SPARC, "SUN SPARC" }, ++ { EM_386, "Intel 80386" }, ++ { EM_68K, "Motorola m68k family" }, ++ { EM_88K, "Motorola m88k family" }, ++ { EM_486, "Intel 80486// Reserved for future use" }, ++ { EM_860, "Intel 80860" }, ++ { EM_MIPS, "MIPS R3000 (officially, big-endian only)" }, ++ { EM_S370, "IBM System/370" }, ++ { EM_MIPS_RS3_LE, ++ "MIPS R3000 little-endian (Oct 4 1999 Draft) Deprecated" }, ++ { EM_res011, "Reserved" }, ++ { EM_res012, "Reserved" }, ++ { EM_res013, "Reserved" }, ++ { EM_res014, "Reserved" }, ++ { EM_PARISC, "HPPA" }, ++ { EM_res016, "Reserved" }, ++ { EM_VPP550, "Fujitsu VPP500" }, ++ { EM_SPARC32PLUS, "Sun's v8plus" }, ++ { EM_960, "Intel 80960" }, ++ { EM_PPC, "PowerPC" }, ++ { EM_PPC64, "64-bit PowerPC" }, ++ { EM_S390, "IBM S/390" }, ++ { EM_SPU, "Sony/Toshiba/IBM SPU" }, ++ { EM_res024, "Reserved" }, ++ { EM_res025, "Reserved" }, ++ { EM_res026, "Reserved" }, ++ { EM_res027, "Reserved" }, ++ { EM_res028, "Reserved" }, ++ { EM_res029, "Reserved" }, ++ { EM_res030, "Reserved" }, ++ { EM_res031, "Reserved" }, ++ { EM_res032, "Reserved" }, ++ { EM_res033, "Reserved" }, ++ { EM_res034, "Reserved" }, ++ { EM_res035, "Reserved" }, ++ { EM_V800, "NEC V800 series" }, ++ { EM_FR20, "Fujitsu FR20" }, ++ { EM_RH32, "TRW RH32" }, ++ { EM_MCORE, "Motorola M*Core // May also be taken by Fujitsu MMA" }, ++ { EM_RCE, "Old name for MCore" }, ++ { EM_ARM, "ARM" }, ++ { EM_OLD_ALPHA, "Digital Alpha" }, ++ { EM_SH, "Renesas (formerly Hitachi) / SuperH SH" }, ++ { EM_SPARCV9, "SPARC v9 64-bit" }, ++ { EM_TRICORE, "Siemens Tricore embedded processor" }, ++ { EM_ARC, "ARC Cores" }, ++ { EM_H8_300, "Renesas (formerly Hitachi) H8/300" }, ++ { EM_H8_300H, "Renesas (formerly Hitachi) H8/300H" }, ++ { EM_H8S, "Renesas (formerly Hitachi) H8S" }, ++ { EM_H8_500, "Renesas (formerly Hitachi) H8/500" }, ++ { EM_IA_64, "Intel IA-64 Processor" }, ++ { EM_MIPS_X, "Stanford MIPS-X" }, ++ { EM_COLDFIRE, "Motorola Coldfire" }, ++ { EM_68HC12, "Motorola M68HC12" }, ++ { EM_MMA, "Fujitsu Multimedia Accelerator" }, ++ { EM_PCP, "Siemens PCP" }, ++ { EM_NCPU, "Sony nCPU embedded RISC processor" }, ++ { EM_NDR1, "Denso NDR1 microprocesspr" }, ++ { EM_STARCORE, "Motorola Star*Core processor" }, ++ { EM_ME16, "Toyota ME16 processor" }, ++ { EM_ST100, "STMicroelectronics ST100 processor" }, ++ { EM_TINYJ, "Advanced Logic Corp. TinyJ embedded processor" }, ++ { EM_X86_64, "Advanced Micro Devices X86-64 processor" }, ++ { EM_PDSP, "Sony DSP Processor" }, ++ { EM_PDP10, "Digital Equipment Corp. PDP-10" }, ++ { EM_PDP11, "Digital Equipment Corp. PDP-11" }, ++ { EM_FX66, "Siemens FX66 microcontroller" }, ++ { EM_ST9PLUS, "STMicroelectronics ST9+ 8/16 bit microcontroller" }, ++ { EM_ST7, "STMicroelectronics ST7 8-bit microcontroller" }, ++ { EM_68HC16, "Motorola MC68HC16 Microcontroller" }, ++ { EM_68HC11, "Motorola MC68HC11 Microcontroller" }, ++ { EM_68HC08, "Motorola MC68HC08 Microcontroller" }, ++ { EM_68HC05, "Motorola MC68HC05 Microcontroller" }, ++ { EM_SVX, "Silicon Graphics SVx" }, ++ { EM_ST19, "STMicroelectronics ST19 8-bit cpu" }, ++ { EM_VAX, "Digital VAX" }, ++ { EM_CRIS, "Axis Communications 32-bit embedded processor" }, ++ { EM_JAVELIN, "Infineon Technologies 32-bit embedded cpu" }, ++ { EM_FIREPATH, "Element 14 64-bit DSP processor" }, ++ { EM_ZSP, "LSI Logic's 16-bit DSP processor" }, ++ { EM_MMIX, "Donald Knuth's educational 64-bit processor" }, ++ { EM_HUANY, "Harvard's machine-independent format" }, ++ { EM_PRISM, "SiTera Prism" }, ++ { EM_AVR, "Atmel AVR 8-bit microcontroller" }, ++ { EM_FR30, "Fujitsu FR30" }, ++ { EM_D10V, "Mitsubishi D10V" }, ++ { EM_D30V, "Mitsubishi D30V" }, ++ { EM_V850, "NEC v850" }, ++ { EM_M32R, "Renesas M32R (formerly Mitsubishi M32R)" }, ++ { EM_MN10300, "Matsushita MN10300" }, ++ { EM_MN10200, "Matsushita MN10200" }, ++ { EM_PJ, "picoJava" }, ++ { EM_OPENRISC, "OpenRISC 32-bit embedded processor" }, ++ { EM_ARC_A5, "ARC Cores Tangent-A5" }, ++ { EM_XTENSA, "Tensilica Xtensa Architecture" }, ++ { EM_VIDEOCORE, "Alphamosaic VideoCore processor" }, ++ { EM_TMM_GPP, "Thompson Multimedia General Purpose Processor" }, ++ { EM_NS32K, "National Semiconductor 32000 series" }, ++ { EM_TPC, "Tenor Network TPC processor" }, ++ { EM_SNP1K, "Trebia SNP 1000 processor" }, ++ { EM_ST200, "STMicroelectronics ST200 microcontroller" }, ++ { EM_IP2K, "Ubicom IP2022 micro controller" }, ++ { EM_MAX, "MAX Processor" }, ++ { EM_CR, "National Semiconductor CompactRISC" }, ++ { EM_F2MC16, "Fujitsu F2MC16" }, ++ { EM_MSP430, "TI msp430 micro controller" }, ++ { EM_BLACKFIN, "ADI Blackfin" }, ++ { EM_SE_C33, "S1C33 Family of Seiko Epson processors" }, ++ { EM_SEP, "Sharp embedded microprocessor" }, ++ { EM_ARCA, "Arca RISC Microprocessor" }, ++ { EM_UNICORE, "Microprocessor series from PKU-Unity Ltd. and MPRC of " ++ "Peking University" }, ++ { EM_EXCESS, "eXcess: 16/32/64-bit configurable embedded CPU" }, ++ { EM_DXP, "Icera Semiconductor Inc. Deep Execution Processor" }, ++ { EM_ALTERA_NIOS2, "Altera Nios II soft-core processor" }, ++ { EM_CRX, "National Semiconductor CRX" }, ++ { EM_XGATE, "Motorola XGATE embedded processor" }, ++ { EM_C166, "Infineon C16x/XC16x processor" }, ++ { EM_M16C, "Renesas M16C series microprocessors" }, ++ { EM_DSPIC30F, "Microchip Technology dsPIC30F Digital Signal Controller" }, ++ { EM_CE, "Freescale Communication Engine RISC core" }, ++ { EM_M32C, "Renesas M32C series microprocessors" }, ++ { EM_res121, "Reserved" }, ++ { EM_res122, "Reserved" }, ++ { EM_res123, "Reserved" }, ++ { EM_res124, "Reserved" }, ++ { EM_res125, "Reserved" }, ++ { EM_res126, "Reserved" }, ++ { EM_res127, "Reserved" }, ++ { EM_res128, "Reserved" }, ++ { EM_res129, "Reserved" }, ++ { EM_res130, "Reserved" }, ++ { EM_TSK3000, "Altium TSK3000 core" }, ++ { EM_RS08, "Freescale RS08 embedded processor" }, ++ { EM_res133, "Reserved" }, ++ { EM_ECOG2, "Cyan Technology eCOG2 microprocessor" }, ++ { EM_SCORE, "Sunplus Score" }, ++ { EM_SCORE7, "Sunplus S+core7 RISC processor" }, ++ { EM_DSP24, "New Japan Radio (NJR) 24-bit DSP Processor" }, ++ { EM_VIDEOCORE3, "Broadcom VideoCore III processor" }, ++ { EM_LATTICEMICO32, "RISC processor for Lattice FPGA architecture" }, ++ { EM_SE_C17, "Seiko Epson C17 family" }, ++ { EM_TI_C6000, "Texas Instruments TMS320C6000 DSP family" }, ++ { EM_TI_C2000, "Texas Instruments TMS320C2000 DSP family" }, ++ { EM_TI_C5500, "Texas Instruments TMS320C55x DSP family" }, ++ { EM_res143, "Reserved" }, ++ { EM_res144, "Reserved" }, ++ { EM_res145, "Reserved" }, ++ { EM_res146, "Reserved" }, ++ { EM_res147, "Reserved" }, ++ { EM_res148, "Reserved" }, ++ { EM_res149, "Reserved" }, ++ { EM_res150, "Reserved" }, ++ { EM_res151, "Reserved" }, ++ { EM_res152, "Reserved" }, ++ { EM_res153, "Reserved" }, ++ { EM_res154, "Reserved" }, ++ { EM_res155, "Reserved" }, ++ { EM_res156, "Reserved" }, ++ { EM_res157, "Reserved" }, ++ { EM_res158, "Reserved" }, ++ { EM_res159, "Reserved" }, ++ { EM_MMDSP_PLUS, "STMicroelectronics 64bit VLIW Data Signal Processor" }, ++ { EM_CYPRESS_M8C, "Cypress M8C microprocessor" }, ++ { EM_R32C, "Renesas R32C series microprocessors" }, ++ { EM_TRIMEDIA, "NXP Semiconductors TriMedia architecture family" }, ++ { EM_QDSP6, "QUALCOMM DSP6 Processor" }, ++ { EM_8051, "Intel 8051 and variants" }, ++ { EM_STXP7X, "STMicroelectronics STxP7x family" }, ++ { EM_NDS32, ++ "Andes Technology compact code size embedded RISC processor family" }, ++ { EM_ECOG1, "Cyan Technology eCOG1X family" }, ++ { EM_ECOG1X, "Cyan Technology eCOG1X family" }, ++ { EM_MAXQ30, "Dallas Semiconductor MAXQ30 Core Micro-controllers" }, ++ { EM_XIMO16, "New Japan Radio (NJR) 16-bit DSP Processor" }, ++ { EM_MANIK, "M2000 Reconfigurable RISC Microprocessor" }, ++ { EM_CRAYNV2, "Cray Inc. NV2 vector architecture" }, ++ { EM_RX, "Renesas RX family" }, ++ { EM_METAG, "Imagination Technologies META processor architecture" }, ++ { EM_MCST_ELBRUS, "MCST Elbrus general purpose hardware architecture" }, ++ { EM_ECOG16, "Cyan Technology eCOG16 family" }, ++ { EM_CR16, "National Semiconductor CompactRISC 16-bit processor" }, ++ { EM_ETPU, "Freescale Extended Time Processing Unit" }, ++ { EM_SLE9X, "Infineon Technologies SLE9X core" }, ++ { EM_L1OM, "Intel L1OM" }, ++ { EM_INTEL181, "Reserved by Intel" }, ++ { EM_INTEL182, "Reserved by Intel" }, ++ { EM_res183, "Reserved by ARM" }, ++ { EM_res184, "Reserved by ARM" }, ++ { EM_AVR32, "Atmel Corporation 32-bit microprocessor family" }, ++ { EM_STM8, "STMicroeletronics STM8 8-bit microcontroller" }, ++ { EM_TILE64, "Tilera TILE64 multicore architecture family" }, ++ { EM_TILEPRO, "Tilera TILEPro multicore architecture family" }, ++ { EM_MICROBLAZE, "Xilinx MicroBlaze 32-bit RISC soft processor core" }, ++ { EM_CUDA, "NVIDIA CUDA architecture " }, ++}; ++ ++static struct section_type_table_t ++{ ++ const Elf64_Half key; ++ const char* str; ++} section_type_table[] = { ++ { SHT_NULL, "NULL" }, ++ { SHT_PROGBITS, "PROGBITS" }, ++ { SHT_SYMTAB, "SYMTAB" }, ++ { SHT_STRTAB, "STRTAB" }, ++ { SHT_RELA, "RELA" }, ++ { SHT_HASH, "HASH" }, ++ { SHT_DYNAMIC, "DYNAMIC" }, ++ { SHT_NOTE, "NOTE" }, ++ { SHT_NOBITS, "NOBITS" }, ++ { SHT_REL, "REL" }, ++ { SHT_SHLIB, "SHLIB" }, ++ { SHT_DYNSYM, "DYNSYM" }, ++ { SHT_INIT_ARRAY, "INIT_ARRAY" }, ++ { SHT_FINI_ARRAY, "FINI_ARRAY" }, ++ { SHT_PREINIT_ARRAY, "PREINIT_ARRAY" }, ++ { SHT_GROUP, "GROUP" }, ++ { SHT_SYMTAB_SHNDX, "SYMTAB_SHNDX " }, ++}; ++ ++static struct segment_type_table_t ++{ ++ const Elf_Word key; ++ const char* str; ++} segment_type_table[] = { ++ { PT_NULL, "NULL" }, { PT_LOAD, "LOAD" }, { PT_DYNAMIC, "DYNAMIC" }, ++ { PT_INTERP, "INTERP" }, { PT_NOTE, "NOTE" }, { PT_SHLIB, "SHLIB" }, ++ { PT_PHDR, "PHDR" }, { PT_TLS, "TLS" }, ++}; ++ ++static struct segment_flag_table_t ++{ ++ const Elf_Word key; ++ const char* str; ++} segment_flag_table[] = { ++ { 0, "" }, { 1, "X" }, { 2, "W" }, { 3, "WX" }, ++ { 4, "R" }, { 5, "RX" }, { 6, "RW" }, { 7, "RWX" }, ++}; ++ ++static struct symbol_bind_t ++{ ++ const Elf_Word key; ++ const char* str; ++} symbol_bind_table[] = { ++ { STB_LOCAL, "LOCAL" }, { STB_GLOBAL, "GLOBAL" }, ++ { STB_WEAK, "WEAK" }, { STB_LOOS, "LOOS" }, ++ { STB_HIOS, "HIOS" }, { STB_MULTIDEF, "MULTIDEF" }, ++ { STB_LOPROC, "LOPROC" }, { STB_HIPROC, "HIPROC" }, ++}; ++ ++static struct symbol_type_t ++{ ++ const Elf_Word key; ++ const char* str; ++} symbol_type_table[] = { ++ { STT_NOTYPE, "NOTYPE" }, { STT_OBJECT, "OBJECT" }, ++ { STT_FUNC, "FUNC" }, { STT_SECTION, "SECTION" }, ++ { STT_FILE, "FILE" }, { STT_COMMON, "COMMON" }, ++ { STT_TLS, "TLS" }, { STT_LOOS, "LOOS" }, ++ { STT_HIOS, "HIOS" }, { STT_LOPROC, "LOPROC" }, ++ { STT_HIPROC, "HIPROC" }, ++}; ++ ++static struct dynamic_tag_t ++{ ++ const Elf_Word key; ++ const char* str; ++} dynamic_tag_table[] = { ++ { DT_NULL, "NULL" }, ++ { DT_NEEDED, "NEEDED" }, ++ { DT_PLTRELSZ, "PLTRELSZ" }, ++ { DT_PLTGOT, "PLTGOT" }, ++ { DT_HASH, "HASH" }, ++ { DT_STRTAB, "STRTAB" }, ++ { DT_SYMTAB, "SYMTAB" }, ++ { DT_RELA, "RELA" }, ++ { DT_RELASZ, "RELASZ" }, ++ { DT_RELAENT, "RELAENT" }, ++ { DT_STRSZ, "STRSZ" }, ++ { DT_SYMENT, "SYMENT" }, ++ { DT_INIT, "INIT" }, ++ { DT_FINI, "FINI" }, ++ { DT_SONAME, "SONAME" }, ++ { DT_RPATH, "RPATH" }, ++ { DT_SYMBOLIC, "SYMBOLIC" }, ++ { DT_REL, "REL" }, ++ { DT_RELSZ, "RELSZ" }, ++ { DT_RELENT, "RELENT" }, ++ { DT_PLTREL, "PLTREL" }, ++ { DT_DEBUG, "DEBUG" }, ++ { DT_TEXTREL, "TEXTREL" }, ++ { DT_JMPREL, "JMPREL" }, ++ { DT_BIND_NOW, "BIND_NOW" }, ++ { DT_INIT_ARRAY, "INIT_ARRAY" }, ++ { DT_FINI_ARRAY, "FINI_ARRAY" }, ++ { DT_INIT_ARRAYSZ, "INIT_ARRAYSZ" }, ++ { DT_FINI_ARRAYSZ, "FINI_ARRAYSZ" }, ++ { DT_RUNPATH, "RUNPATH" }, ++ { DT_FLAGS, "FLAGS" }, ++ { DT_ENCODING, "ENCODING" }, ++ { DT_PREINIT_ARRAY, "PREINIT_ARRAY" }, ++ { DT_PREINIT_ARRAYSZ, "PREINIT_ARRAYSZ" }, ++ { DT_MAXPOSTAGS, "MAXPOSTAGS" }, ++}; ++ ++static const ELFIO::Elf_Xword MAX_DATA_ENTRIES = 64; ++ ++//------------------------------------------------------------------------------ ++class dump ++{ ++#define DUMP_DEC_FORMAT( width ) \ ++ std::setw( width ) << std::setfill( ' ' ) << std::dec << std::right ++#define DUMP_HEX_FORMAT( width ) \ ++ std::setw( width ) << std::setfill( '0' ) << std::hex << std::right ++#define DUMP_STR_FORMAT( width ) \ ++ std::setw( width ) << std::setfill( ' ' ) << std::hex << std::left ++ ++ public: ++ //------------------------------------------------------------------------------ ++ static void header( std::ostream& out, const elfio& reader ) ++ { ++ if ( !reader.get_header_size() ) { ++ return; ++ } ++ out << "ELF Header" << std::endl ++ << std::endl ++ << " Class: " << str_class( reader.get_class() ) << std::endl ++ << " Encoding: " << str_endian( reader.get_encoding() ) ++ << std::endl ++ << " ELFVersion: " << str_version( reader.get_elf_version() ) ++ << std::endl ++ << " Type: " << str_type( reader.get_type() ) << std::endl ++ << " Machine: " << str_machine( reader.get_machine() ) ++ << std::endl ++ << " Version: " << str_version( reader.get_version() ) ++ << std::endl ++ << " Entry: " ++ << "0x" << std::hex << reader.get_entry() << std::endl ++ << " Flags: " ++ << "0x" << std::hex << reader.get_flags() << std::endl ++ << std::endl; ++ } ++ ++ //------------------------------------------------------------------------------ ++ static void section_headers( std::ostream& out, const elfio& reader ) ++ { ++ Elf_Half n = reader.sections.size(); ++ ++ if ( n == 0 ) { ++ return; ++ } ++ ++ out << "Section Headers:" << std::endl; ++ if ( reader.get_class() == ELFCLASS32 ) { // Output for 32-bit ++ out << "[ Nr ] Type Addr Size ES Flg Lk Inf " ++ "Al Name" ++ << std::endl; ++ } ++ else { // Output for 64-bit ++ out << "[ Nr ] Type Addr Size " ++ " ES Flg" ++ << std::endl ++ << " Lk Inf Al Name" << std::endl; ++ } ++ ++ for ( Elf_Half i = 0; i < n; ++i ) { // For all sections ++ section* sec = reader.sections[i]; ++ section_header( out, i, sec, reader.get_class() ); ++ } ++ ++ out << "Key to Flags: W (write), A (alloc), X (execute)\n\n" ++ << std::endl; ++ } ++ ++ //------------------------------------------------------------------------------ ++ static void section_header( std::ostream& out, ++ Elf_Half no, ++ const section* sec, ++ unsigned char elf_class ) ++ { ++ std::ios_base::fmtflags original_flags = out.flags(); ++ ++ if ( elf_class == ELFCLASS32 ) { // Output for 32-bit ++ out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " ++ << DUMP_STR_FORMAT( 17 ) << str_section_type( sec->get_type() ) ++ << " " << DUMP_HEX_FORMAT( 8 ) << sec->get_address() << " " ++ << DUMP_HEX_FORMAT( 8 ) << sec->get_size() << " " ++ << DUMP_HEX_FORMAT( 2 ) << sec->get_entry_size() << " " ++ << DUMP_STR_FORMAT( 3 ) << section_flags( sec->get_flags() ) ++ << " " << DUMP_HEX_FORMAT( 2 ) << sec->get_link() << " " ++ << DUMP_HEX_FORMAT( 3 ) << sec->get_info() << " " ++ << DUMP_HEX_FORMAT( 2 ) << sec->get_addr_align() << " " ++ << DUMP_STR_FORMAT( 17 ) << sec->get_name() << " " << std::endl; ++ } ++ else { // Output for 64-bit ++ out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " ++ << DUMP_STR_FORMAT( 17 ) << str_section_type( sec->get_type() ) ++ << " " << DUMP_HEX_FORMAT( 16 ) << sec->get_address() << " " ++ << DUMP_HEX_FORMAT( 16 ) << sec->get_size() << " " ++ << DUMP_HEX_FORMAT( 4 ) << sec->get_entry_size() << " " ++ << DUMP_STR_FORMAT( 3 ) << section_flags( sec->get_flags() ) ++ << " " << std::endl ++ << " " << DUMP_HEX_FORMAT( 4 ) << sec->get_link() << " " ++ << DUMP_HEX_FORMAT( 4 ) << sec->get_info() << " " ++ << DUMP_HEX_FORMAT( 4 ) << sec->get_addr_align() << " " ++ << DUMP_STR_FORMAT( 17 ) << sec->get_name() << " " << std::endl; ++ } ++ ++ out.flags( original_flags ); ++ ++ return; ++ } ++ ++ //------------------------------------------------------------------------------ ++ static void segment_headers( std::ostream& out, const elfio& reader ) ++ { ++ Elf_Half n = reader.segments.size(); ++ if ( n == 0 ) { ++ return; ++ } ++ ++ out << "Segment headers:" << std::endl; ++ if ( reader.get_class() == ELFCLASS32 ) { // Output for 32-bit ++ out << "[ Nr ] Type VirtAddr PhysAddr FileSize Mem.Size " ++ "Flags Align" ++ << std::endl; ++ } ++ else { // Output for 64-bit ++ out << "[ Nr ] Type VirtAddr PhysAddr " ++ "Flags" ++ << std::endl ++ << " FileSize Mem.Size " ++ "Align" ++ << std::endl; ++ } ++ ++ for ( Elf_Half i = 0; i < n; ++i ) { ++ segment* seg = reader.segments[i]; ++ segment_header( out, i, seg, reader.get_class() ); ++ } ++ ++ out << std::endl; ++ } ++ ++ //------------------------------------------------------------------------------ ++ static void segment_header( std::ostream& out, ++ Elf_Half no, ++ const segment* seg, ++ unsigned int elf_class ) ++ { ++ std::ios_base::fmtflags original_flags = out.flags(); ++ ++ if ( elf_class == ELFCLASS32 ) { // Output for 32-bit ++ out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " ++ << DUMP_STR_FORMAT( 14 ) << str_segment_type( seg->get_type() ) ++ << " " << DUMP_HEX_FORMAT( 8 ) << seg->get_virtual_address() ++ << " " << DUMP_HEX_FORMAT( 8 ) << seg->get_physical_address() ++ << " " << DUMP_HEX_FORMAT( 8 ) << seg->get_file_size() << " " ++ << DUMP_HEX_FORMAT( 8 ) << seg->get_memory_size() << " " ++ << DUMP_STR_FORMAT( 8 ) << str_segment_flag( seg->get_flags() ) ++ << " " << DUMP_HEX_FORMAT( 8 ) << seg->get_align() << " " ++ << std::endl; ++ } ++ else { // Output for 64-bit ++ out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " ++ << DUMP_STR_FORMAT( 14 ) << str_segment_type( seg->get_type() ) ++ << " " << DUMP_HEX_FORMAT( 16 ) << seg->get_virtual_address() ++ << " " << DUMP_HEX_FORMAT( 16 ) << seg->get_physical_address() ++ << " " << DUMP_STR_FORMAT( 16 ) ++ << str_segment_flag( seg->get_flags() ) << " " << std::endl ++ << " " << DUMP_HEX_FORMAT( 16 ) ++ << seg->get_file_size() << " " << DUMP_HEX_FORMAT( 16 ) ++ << seg->get_memory_size() << " " << DUMP_HEX_FORMAT( 16 ) ++ << seg->get_align() << " " << std::endl; ++ } ++ ++ out.flags( original_flags ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ static void symbol_tables( std::ostream& out, const elfio& reader ) ++ { ++ Elf_Half n = reader.sections.size(); ++ for ( Elf_Half i = 0; i < n; ++i ) { // For all sections ++ section* sec = reader.sections[i]; ++ if ( SHT_SYMTAB == sec->get_type() || ++ SHT_DYNSYM == sec->get_type() ) { ++ symbol_section_accessor symbols( reader, sec ); ++ ++ Elf_Xword sym_no = symbols.get_symbols_num(); ++ if ( sym_no > 0 ) { ++ out << "Symbol table (" << sec->get_name() << ")" ++ << std::endl; ++ if ( reader.get_class() == ++ ELFCLASS32 ) { // Output for 32-bit ++ out << "[ Nr ] Value Size Type Bind " ++ "Sect Name" ++ << std::endl; ++ } ++ else { // Output for 64-bit ++ out << "[ Nr ] Value Size Type " ++ " Bind Sect" ++ << std::endl ++ << " Name" << std::endl; ++ } ++ for ( Elf_Xword i = 0; i < sym_no; ++i ) { ++ std::string name; ++ Elf64_Addr value = 0; ++ Elf_Xword size = 0; ++ unsigned char bind = 0; ++ unsigned char type = 0; ++ Elf_Half section = 0; ++ unsigned char other = 0; ++ symbols.get_symbol( i, name, value, size, bind, type, ++ section, other ); ++ symbol_table( out, i, name, value, size, bind, type, ++ section, reader.get_class() ); ++ } ++ ++ out << std::endl; ++ } ++ } ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ static void symbol_table( std::ostream& out, ++ Elf_Xword no, ++ std::string& name, ++ Elf64_Addr value, ++ Elf_Xword size, ++ unsigned char bind, ++ unsigned char type, ++ Elf_Half section, ++ unsigned int elf_class ) ++ { ++ std::ios_base::fmtflags original_flags = out.flags(); ++ ++ if ( elf_class == ELFCLASS32 ) { // Output for 32-bit ++ out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " ++ << DUMP_HEX_FORMAT( 8 ) << value << " " << DUMP_HEX_FORMAT( 8 ) ++ << size << " " << DUMP_STR_FORMAT( 7 ) ++ << str_symbol_type( type ) << " " << DUMP_STR_FORMAT( 8 ) ++ << str_symbol_bind( bind ) << " " << DUMP_DEC_FORMAT( 5 ) ++ << section << " " << DUMP_STR_FORMAT( 1 ) << name << " " ++ << std::endl; ++ } ++ else { // Output for 64-bit ++ out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " ++ << DUMP_HEX_FORMAT( 16 ) << value << " " ++ << DUMP_HEX_FORMAT( 16 ) << size << " " << DUMP_STR_FORMAT( 7 ) ++ << str_symbol_type( type ) << " " << DUMP_STR_FORMAT( 8 ) ++ << str_symbol_bind( bind ) << " " << DUMP_DEC_FORMAT( 5 ) ++ << section << " " << std::endl ++ << " " << DUMP_STR_FORMAT( 1 ) << name << " " ++ << std::endl; ++ } ++ ++ out.flags( original_flags ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ static void notes( std::ostream& out, const elfio& reader ) ++ { ++ Elf_Half no = reader.sections.size(); ++ for ( Elf_Half i = 0; i < no; ++i ) { // For all sections ++ section* sec = reader.sections[i]; ++ if ( SHT_NOTE == sec->get_type() ) { // Look at notes ++ note_section_accessor notes( reader, sec ); ++ Elf_Word no_notes = notes.get_notes_num(); ++ if ( no > 0 ) { ++ out << "Note section (" << sec->get_name() << ")" ++ << std::endl ++ << " No Type Name" << std::endl; ++ for ( Elf_Word j = 0; j < no_notes; ++j ) { // For all notes ++ Elf_Word type; ++ std::string name; ++ void* desc; ++ Elf_Word descsz; ++ ++ if ( notes.get_note( j, type, name, desc, descsz ) ) { ++ // 'name' usually contains \0 at the end. Try to fix it ++ name = name.c_str(); ++ note( out, j, type, name ); ++ } ++ } ++ ++ out << std::endl; ++ } ++ } ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ static void modinfo( std::ostream& out, const elfio& reader ) ++ { ++ Elf_Half no = reader.sections.size(); ++ for ( Elf_Half i = 0; i < no; ++i ) { // For all sections ++ section* sec = reader.sections[i]; ++ if ( ".modinfo" == sec->get_name() ) { // Look for the section ++ out << "Section .modinfo" << std::endl; ++ ++ const_modinfo_section_accessor modinfo( sec ); ++ for ( Elf_Word i = 0; i < modinfo.get_attribute_num(); i++ ) { ++ std::string field; ++ std::string value; ++ if ( modinfo.get_attribute( i, field, value ) ) { ++ out << " " << std::setw( 20 ) << field ++ << std::setw( 0 ) << " = " << value << std::endl; ++ } ++ } ++ ++ out << std::endl; ++ break; ++ } ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ static void ++ note( std::ostream& out, int no, Elf_Word type, const std::string& name ) ++ { ++ out << " [" << DUMP_DEC_FORMAT( 2 ) << no << "] " ++ << DUMP_HEX_FORMAT( 8 ) << type << " " << DUMP_STR_FORMAT( 1 ) ++ << name << std::endl; ++ } ++ ++ //------------------------------------------------------------------------------ ++ static void dynamic_tags( std::ostream& out, const elfio& reader ) ++ { ++ Elf_Half n = reader.sections.size(); ++ for ( Elf_Half i = 0; i < n; ++i ) { // For all sections ++ section* sec = reader.sections[i]; ++ if ( SHT_DYNAMIC == sec->get_type() ) { ++ dynamic_section_accessor dynamic( reader, sec ); ++ ++ Elf_Xword dyn_no = dynamic.get_entries_num(); ++ if ( dyn_no > 0 ) { ++ out << "Dynamic section (" << sec->get_name() << ")" ++ << std::endl; ++ out << "[ Nr ] Tag Name/Value" << std::endl; ++ for ( Elf_Xword i = 0; i < dyn_no; ++i ) { ++ Elf_Xword tag = 0; ++ Elf_Xword value = 0; ++ std::string str; ++ dynamic.get_entry( i, tag, value, str ); ++ dynamic_tag( out, i, tag, value, str, ++ reader.get_class() ); ++ if ( DT_NULL == tag ) { ++ break; ++ } ++ } ++ ++ out << std::endl; ++ } ++ } ++ } ++ } ++ ++ //------------------------------------------------------------------------------ ++ static void dynamic_tag( std::ostream& out, ++ Elf_Xword no, ++ Elf_Xword tag, ++ Elf_Xword value, ++ std::string str, ++ unsigned int /*elf_class*/ ) ++ { ++ out << "[" << DUMP_DEC_FORMAT( 5 ) << no << "] " ++ << DUMP_STR_FORMAT( 16 ) << str_dynamic_tag( tag ) << " "; ++ if ( str.empty() ) { ++ out << DUMP_HEX_FORMAT( 16 ) << value << " "; ++ } ++ else { ++ out << DUMP_STR_FORMAT( 32 ) << str << " "; ++ } ++ out << std::endl; ++ } ++ ++ //------------------------------------------------------------------------------ ++ static void section_data( std::ostream& out, const section* sec ) ++ { ++ std::ios_base::fmtflags original_flags = out.flags(); ++ ++ out << sec->get_name() << std::endl; ++ const char* pdata = sec->get_data(); ++ if ( pdata ) { ++ ELFIO::Elf_Xword i; ++ for ( i = 0; i < std::min( sec->get_size(), MAX_DATA_ENTRIES ); ++ ++i ) { ++ if ( i % 16 == 0 ) { ++ out << "[" << DUMP_HEX_FORMAT( 8 ) << i << "]"; ++ } ++ ++ out << " " << DUMP_HEX_FORMAT( 2 ) << ( pdata[i] & 0x000000FF ); ++ ++ if ( i % 16 == 15 ) { ++ out << std::endl; ++ } ++ } ++ if ( i % 16 != 0 ) { ++ out << std::endl; ++ } ++ ++ out.flags( original_flags ); ++ } ++ ++ return; ++ } ++ ++ //------------------------------------------------------------------------------ ++ static void section_datas( std::ostream& out, const elfio& reader ) ++ { ++ Elf_Half n = reader.sections.size(); ++ ++ if ( n == 0 ) { ++ return; ++ } ++ ++ out << "Section Data:" << std::endl; ++ ++ for ( Elf_Half i = 1; i < n; ++i ) { // For all sections ++ section* sec = reader.sections[i]; ++ if ( sec->get_type() == SHT_NOBITS ) { ++ continue; ++ } ++ section_data( out, sec ); ++ } ++ ++ out << std::endl; ++ } ++ ++ //------------------------------------------------------------------------------ ++ static void ++ segment_data( std::ostream& out, Elf_Half no, const segment* seg ) ++ { ++ std::ios_base::fmtflags original_flags = out.flags(); ++ ++ out << "Segment # " << no << std::endl; ++ const char* pdata = seg->get_data(); ++ if ( pdata ) { ++ ELFIO::Elf_Xword i; ++ for ( i = 0; i < std::min( seg->get_file_size(), MAX_DATA_ENTRIES ); ++ ++i ) { ++ if ( i % 16 == 0 ) { ++ out << "[" << DUMP_HEX_FORMAT( 8 ) << i << "]"; ++ } ++ ++ out << " " << DUMP_HEX_FORMAT( 2 ) << ( pdata[i] & 0x000000FF ); ++ ++ if ( i % 16 == 15 ) { ++ out << std::endl; ++ } ++ } ++ if ( i % 16 != 0 ) { ++ out << std::endl; ++ } ++ ++ out.flags( original_flags ); ++ } ++ ++ return; ++ } ++ ++ //------------------------------------------------------------------------------ ++ static void segment_datas( std::ostream& out, const elfio& reader ) ++ { ++ Elf_Half n = reader.segments.size(); ++ ++ if ( n == 0 ) { ++ return; ++ } ++ ++ out << "Segment Data:" << std::endl; ++ ++ for ( Elf_Half i = 0; i < n; ++i ) { // For all sections ++ segment* seg = reader.segments[i]; ++ segment_data( out, i, seg ); ++ } ++ ++ out << std::endl; ++ } ++ ++ private: ++ //------------------------------------------------------------------------------ ++ template <typename T, typename K> ++ std::string static find_value_in_table( const T& table, const K& key ) ++ { ++ std::string res = "?"; ++ for ( unsigned int i = 0; i < sizeof( table ) / sizeof( table[0] ); ++ ++i ) { ++ if ( table[i].key == key ) { ++ res = table[i].str; ++ break; ++ } ++ } ++ ++ return res; ++ } ++ ++ //------------------------------------------------------------------------------ ++ template <typename T, typename K> ++ static std::string format_assoc( const T& table, const K& key ) ++ { ++ std::string str = find_value_in_table( table, key ); ++ if ( str == "?" ) { ++ std::ostringstream oss; ++ oss << str << " (0x" << std::hex << key << ")"; ++ str = oss.str(); ++ } ++ ++ return str; ++ } ++ ++ //------------------------------------------------------------------------------ ++ template <typename T> ++ static std::string format_assoc( const T& table, const char key ) ++ { ++ return format_assoc( table, (const int)key ); ++ } ++ ++ //------------------------------------------------------------------------------ ++ static std::string section_flags( Elf_Xword flags ) ++ { ++ std::string ret = ""; ++ if ( flags & SHF_WRITE ) { ++ ret += "W"; ++ } ++ if ( flags & SHF_ALLOC ) { ++ ret += "A"; ++ } ++ if ( flags & SHF_EXECINSTR ) { ++ ret += "X"; ++ } ++ ++ return ret; ++ } ++ ++//------------------------------------------------------------------------------ ++#define STR_FUNC_TABLE( name ) \ ++ template <typename T> static std::string str_##name( const T key ) \ ++ { \ ++ return format_assoc( name##_table, key ); \ ++ } ++ ++ STR_FUNC_TABLE( class ) ++ STR_FUNC_TABLE( endian ) ++ STR_FUNC_TABLE( version ) ++ STR_FUNC_TABLE( type ) ++ STR_FUNC_TABLE( machine ) ++ STR_FUNC_TABLE( section_type ) ++ STR_FUNC_TABLE( segment_type ) ++ STR_FUNC_TABLE( segment_flag ) ++ STR_FUNC_TABLE( symbol_bind ) ++ STR_FUNC_TABLE( symbol_type ) ++ STR_FUNC_TABLE( dynamic_tag ) ++ ++#undef STR_FUNC_TABLE ++#undef DUMP_DEC_FORMAT ++#undef DUMP_HEX_FORMAT ++#undef DUMP_STR_FORMAT ++}; // class dump ++ ++}; // namespace ELFIO ++ ++#endif // ELFIO_DUMP_HPP ++ ++/*** End of inlined file: elfio_dump.hpp ***/ ++ +diff --git a/tests/kt-gtest/kt-test-utils/cpp-stub/stub.h b/tests/kt-gtest/kt-test-utils/cpp-stub/stub.h +new file mode 100755 +index 0000000..c5f2f53 +--- /dev/null ++++ b/tests/kt-gtest/kt-test-utils/cpp-stub/stub.h +@@ -0,0 +1,360 @@ ++#ifndef __STUB_H__ ++#define __STUB_H__ ++ ++#ifdef _WIN32 ++//windows ++#include <windows.h> ++#include <processthreadsapi.h> ++#else ++//linux ++#include <unistd.h> ++#include <bits/stdint-uintn.h> ++#include <sys/mman.h> ++#endif ++//c ++#include <cstddef> ++#include <cstring> ++//c++ ++#include <map> ++ ++ ++#define ADDR(CLASS_NAME,MEMBER_NAME) (&CLASS_NAME::MEMBER_NAME) ++ ++/********************************************************** ++ replace function ++**********************************************************/ ++#ifdef _WIN32 ++#define CACHEFLUSH(addr, size) FlushInstructionCache(GetCurrentProcess(), addr, size) ++#else ++#define CACHEFLUSH(addr, size) __builtin___clear_cache(addr, addr + size) ++#endif ++ ++#if defined(__aarch64__) || defined(_M_ARM64) ++ #define CODESIZE 16U ++ #define CODESIZE_MIN 16U ++ #define CODESIZE_MAX CODESIZE ++ // ldr x9, +8 ++ // br x9 ++ // addr ++ #define REPLACE_FAR(t, fn, fn_stub)\ ++ ((uint32_t*)fn)[0] = 0x58000040 | 9;\ ++ ((uint32_t*)fn)[1] = 0xd61f0120 | (9 << 5);\ ++ *(long long *)(fn + 8) = (long long )fn_stub;\ ++ CACHEFLUSH((char *)fn, CODESIZE); ++ #define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub) ++#elif defined(__arm__) || defined(_M_ARM) ++ #define CODESIZE 8U ++ #define CODESIZE_MIN 8U ++ #define CODESIZE_MAX CODESIZE ++ // ldr pc, [pc, #-4] ++ #define REPLACE_FAR(t, fn, fn_stub)\ ++ ((uint32_t*)fn)[0] = 0xe51ff004;\ ++ ((uint32_t*)fn)[1] = (uint32_t)fn_stub;\ ++ CACHEFLUSH((char *)fn, CODESIZE); ++ #define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub) ++#elif defined(__mips64) ++ #define CACHEFLUSH(addr, size) __builtin___clear_cache(addr, addr + size) ++ #define CODESIZE 80U ++ #define CODESIZE_MIN 80U ++ #define CODESIZE_MAX CODESIZE ++ //mips没有PC指针,所以需要手动入栈出栈 ++ //120000ce0: 67bdffe0 daddiu sp, sp, -32 //入栈 ++ //120000ce4: ffbf0018 sd ra, 24(sp) ++ //120000ce8: ffbe0010 sd s8, 16(sp) ++ //120000cec: ffbc0008 sd gp, 8(sp) ++ //120000cf0: 03a0f025 move s8, sp ++ ++ //120000d2c: 03c0e825 move sp, s8 //出栈 ++ //120000d30: dfbf0018 ld ra, 24(sp) ++ //120000d34: dfbe0010 ld s8, 16(sp) ++ //120000d38: dfbc0008 ld gp, 8(sp) ++ //120000d3c: 67bd0020 daddiu sp, sp, 32 ++ //120000d40: 03e00008 jr ra ++ ++ #define REPLACE_FAR(t, fn, fn_stub)\ ++ ((uint32_t *)fn)[0] = 0x67bdffe0;\ ++ ((uint32_t *)fn)[1] = 0xffbf0018;\ ++ ((uint32_t *)fn)[2] = 0xffbe0010;\ ++ ((uint32_t *)fn)[3] = 0xffbc0008;\ ++ ((uint32_t *)fn)[4] = 0x03a0f025;\ ++ *(uint16_t *)(fn + 20) = (long long)fn_stub >> 32;\ ++ *(fn + 22) = 0x19;\ ++ *(fn + 23) = 0x24;\ ++ ((uint32_t *)fn)[6] = 0x0019cc38;\ ++ *(uint16_t *)(fn + 28) = (long long)fn_stub >> 16;\ ++ *(fn + 30) = 0x39;\ ++ *(fn + 31) = 0x37;\ ++ ((uint32_t *)fn)[8] = 0x0019cc38;\ ++ *(uint16_t *)(fn + 36) = (long long)fn_stub;\ ++ *(fn + 38) = 0x39;\ ++ *(fn + 39) = 0x37;\ ++ ((uint32_t *)fn)[10] = 0x0320f809;\ ++ ((uint32_t *)fn)[11] = 0x00000000;\ ++ ((uint32_t *)fn)[12] = 0x00000000;\ ++ ((uint32_t *)fn)[13] = 0x03c0e825;\ ++ ((uint32_t *)fn)[14] = 0xdfbf0018;\ ++ ((uint32_t *)fn)[15] = 0xdfbe0010;\ ++ ((uint32_t *)fn)[16] = 0xdfbc0008;\ ++ ((uint32_t *)fn)[17] = 0x67bd0020;\ ++ ((uint32_t *)fn)[18] = 0x03e00008;\ ++ ((uint32_t *)fn)[19] = 0x00000000;\ ++ CACHEFLUSH((char *)fn, CODESIZE); ++ #define REPLACE_NEAR(t, fn, fn_stub) REPLACE_FAR(t, fn, fn_stub) ++#elif defined(__thumb__) || defined(_M_THUMB) ++ #error "Thumb is not supported" ++#else //__i386__ _x86_64__ ++ #define CODESIZE 13U ++ #define CODESIZE_MIN 5U ++ #define CODESIZE_MAX CODESIZE ++ //13 byte(jmp m16:64) ++ //movabs $0x102030405060708,%r11 ++ //jmpq *%r11 ++ static void REPLACE_FAR(void *t, char *fn, char *fn_stub) ++ { ++ *fn = 0x49; ++ *(fn + 1) = 0xbb; ++ *(long long *)(fn + 2) = (long long)fn_stub; ++ *(fn + 10) = 0x41; ++ *(fn + 11) = 0xff; ++ *(fn + 12) = 0xe3; ++ CACHEFLUSH((char *)fn, CODESIZE); ++ } ++ //5 byte(jmp rel32) ++ #define REPLACE_NEAR(t, fn, fn_stub)\ ++ *fn = 0xE9;\ ++ *(int *)(fn + 1) = (int)(fn_stub - fn - CODESIZE_MIN);\ ++ CACHEFLUSH((char *)fn, CODESIZE); ++#endif ++ ++struct func_stub ++{ ++ char *fn; ++ unsigned char code_buf[CODESIZE]; ++ bool far_jmp; ++}; ++ ++class Stub ++{ ++public: ++ Stub() ++ { ++#ifdef _WIN32 ++ SYSTEM_INFO sys_info; ++ GetSystemInfo(&sys_info); ++ m_pagesize = sys_info.dwPageSize; ++#else ++ m_pagesize = sysconf(_SC_PAGE_SIZE); ++#endif ++ ++ if (m_pagesize < 0) ++ { ++ m_pagesize = 4096; ++ } ++ } ++ ~Stub() ++ { ++ clear(); ++ } ++ ++ virtual void clear() ++ { ++ std::map<char*,func_stub*>::iterator iter; ++ struct func_stub *pstub; ++ for(iter=m_result.begin(); iter != m_result.end(); iter++) ++ { ++ pstub = iter->second; ++#ifdef _WIN32 ++ DWORD lpflOldProtect; ++ if(0 != VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect)) ++#else ++ if (0 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC)) ++#endif ++ { ++ ++ if(pstub->far_jmp) ++ { ++ std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MAX); ++ } ++ else ++ { ++ std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MIN); ++ } ++ ++#ifdef _WIN32 ++ VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect); ++#else ++ CACHEFLUSH(pstub->fn,CODESIZE); ++ mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC); ++#endif ++ } ++ ++ iter->second = NULL; ++ delete pstub; ++ } ++ ++ m_result.clear(); ++ return; ++ } ++ template<typename T,typename S> ++ bool set(T addr, S addr_stub) ++ { ++ char * fn; ++ char * fn_stub; ++ fn = addrof(addr); ++ fn_stub = addrof(addr_stub); ++ struct func_stub *pstub; ++ std::map<char*,func_stub*>::iterator iter = m_result.find(fn); ++ ++ if (iter == m_result.end()) ++ { ++ pstub = new func_stub; ++ //start ++ pstub->fn = fn; ++ ++ if(distanceof(fn, fn_stub)) ++ { ++ pstub->far_jmp = true; ++ std::memcpy(pstub->code_buf, fn, CODESIZE_MAX); ++ } ++ else ++ { ++ pstub->far_jmp = false; ++ std::memcpy(pstub->code_buf, fn, CODESIZE_MIN); ++ } ++ } ++ else { ++ pstub = iter->second; ++ pstub->far_jmp = distanceof(fn, fn_stub); ++ } ++ ++ ++ ++#ifdef _WIN32 ++ DWORD lpflOldProtect; ++ if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect)) ++#else ++ if (-1 == mprotect(pageof(pstub->fn), static_cast<size_t>(m_pagesize * 2), PROT_READ | PROT_WRITE | PROT_EXEC)) ++#endif ++ { ++ throw("stub set memory protect to w+r+x faild"); ++ return false; ++ } ++ ++ if(pstub->far_jmp) ++ { ++ REPLACE_FAR(this, fn, fn_stub); ++ } ++ else ++ { ++ REPLACE_NEAR(this, fn, fn_stub); ++ } ++ ++ ++#ifdef _WIN32 ++ if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect)) ++#else ++ if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC)) ++#endif ++ { ++ throw("stub set memory protect to r+x failed"); ++ return false; ++ } ++ m_result.insert(std::pair<char*,func_stub*>(fn,pstub)); ++ return true; ++ } ++ ++ template<typename T> ++ bool reset(T addr) ++ { ++ char * fn; ++ fn = addrof(addr); ++ ++ std::map<char*,func_stub*>::iterator iter = m_result.find(fn); ++ ++ if (iter == m_result.end()) ++ { ++ return true; ++ } ++ struct func_stub *pstub; ++ pstub = iter->second; ++ ++#ifdef _WIN32 ++ DWORD lpflOldProtect; ++ if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READWRITE, &lpflOldProtect)) ++#else ++ if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC)) ++#endif ++ { ++ throw("stub reset memory protect to w+r+x faild"); ++ return false; ++ } ++ ++ if(pstub->far_jmp) ++ { ++ std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MAX); ++ } ++ else ++ { ++ std::memcpy(pstub->fn, pstub->code_buf, CODESIZE_MIN); ++ } ++ ++#ifdef _WIN32 ++ if(0 == VirtualProtect(pageof(pstub->fn), m_pagesize * 2, PAGE_EXECUTE_READ, &lpflOldProtect)) ++#else ++ CACHEFLUSH(pstub->fn,CODESIZE); ++ if (-1 == mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_EXEC)) ++#endif ++ { ++ throw("stub reset memory protect to r+x failed"); ++ return false; ++ } ++ ++ m_result.erase(iter); ++ delete pstub; ++ ++ return true; ++ } ++protected: ++ char *pageof(char* addr) ++ { ++#ifdef _WIN32 ++ return (char *)((unsigned long long)addr & ~(m_pagesize - 1)); ++#else ++ return (char *)((unsigned long)addr & ~(m_pagesize - 1)); ++#endif ++ } ++ ++ template<typename T> ++ char* addrof(T addr) ++ { ++ union ++ { ++ T _s; ++ char* _d; ++ }ut; ++ ut._s = addr; ++ return ut._d; ++ } ++ ++ bool distanceof(char* addr, char* addr_stub) ++ { ++ std::ptrdiff_t diff = addr_stub >= addr ? addr_stub - addr : addr - addr_stub; ++ if((sizeof(addr) > 4) && (((diff >> 31) - 1) > 0)) ++ { ++ return true; ++ } ++ return false; ++ } ++ ++protected: ++#ifdef _WIN32 ++ //LLP64 ++ long long m_pagesize; ++#else ++ //LP64 ++ long m_pagesize; ++#endif ++ std::map<char*, func_stub*> m_result; ++}; ++ ++#endif +diff --git a/tests/kt-gtest/kt-test-utils/stub-ext/stub-shadow.cpp b/tests/kt-gtest/kt-test-utils/stub-ext/stub-shadow.cpp +new file mode 100755 +index 0000000..d49ede2 +--- /dev/null ++++ b/tests/kt-gtest/kt-test-utils/stub-ext/stub-shadow.cpp +@@ -0,0 +1,31 @@ ++#include "stub-shadow.h" ++ ++namespace stub_ext { ++ ++WrapperMap stub_wrappers; ++ ++Wrapper::Wrapper() ++{ ++ ++} ++ ++Wrapper::~Wrapper() ++{ ++ ++} ++ ++void freeWrapper(Wrapper *wrapper) ++{ ++ if (!wrapper) ++ return; ++ ++ for (auto iter = stub_wrappers.begin(); iter != stub_wrappers.end();) { ++ if (iter->second == wrapper) ++ iter = stub_wrappers.erase(iter); ++ else ++ ++iter; ++ } ++ ++ delete wrapper; ++} ++} +diff --git a/tests/kt-gtest/kt-test-utils/stub-ext/stub-shadow.h b/tests/kt-gtest/kt-test-utils/stub-ext/stub-shadow.h +new file mode 100755 +index 0000000..732600a +--- /dev/null ++++ b/tests/kt-gtest/kt-test-utils/stub-ext/stub-shadow.h +@@ -0,0 +1,145 @@ ++#ifndef STUBSHADOW_H ++#define STUBSHADOW_H ++ ++#include <unordered_map> ++#include <assert.h> ++ ++namespace stub_ext { ++ ++#define LAMDA_FUNCTION_TYPE decltype(&Lamda::operator()) ++ ++class Wrapper ++{ ++public: ++ Wrapper(); ++ virtual ~Wrapper(); ++}; ++ ++typedef std::unordered_map<long, Wrapper* > WrapperMap; ++extern WrapperMap stub_wrappers; ++ ++template<class Lamda> ++class LamdaWrapper : public Wrapper ++{ ++public: ++ LamdaWrapper(Lamda func): Wrapper(),_func(func){} ++ ~LamdaWrapper(){} ++ Lamda _func; ++}; ++ ++template <typename Func> ++struct VFLocator ++{ ++ ++}; ++ ++template <class Obj, typename Ret, typename... Args> ++struct VFLocator<Ret (Obj::*)(Args...)> ++{ ++ typedef Ret (*Func)(Obj*, Args...); ++}; ++ ++template <class Obj, typename Ret, typename... Args> ++struct VFLocator<Ret (Obj::*)(Args...) const> ++{ ++ typedef Ret (*Func)(Obj*, Args...); ++}; ++ ++template <typename Func> ++struct LamdaCaller ++{ ++ ++}; ++ ++template <class Obj, typename Ret, typename... Args> ++struct LamdaCaller<Ret (Obj::*)(Args...) const> ++{ ++ template<class Lamda, typename ...OrgArgs> ++ static Ret call(LamdaWrapper<Lamda> *wrapper, OrgArgs&&... args) ++ { ++ return wrapper->_func(std::forward<OrgArgs>(args)...); ++ } ++}; ++ ++template <class Obj, typename Ret> ++struct LamdaCaller<Ret (Obj::*)() const> ++{ ++ template<class Lamda, typename ...OrgArgs> ++ static Ret call(LamdaWrapper<Lamda> *wrapper, OrgArgs&&... args) ++ { ++ return wrapper->_func(); ++ } ++}; ++ ++template<typename Func, class Lamda> ++struct FuncShadow ++{ ++ ++}; ++ ++template<typename Ret, typename... Args, class Lamda> ++struct FuncShadow<Ret (*)(Args...), Lamda> ++{ ++ typedef Ret (*Shadow)(Args...); ++ typedef Ret RetType; ++ ++ static Ret call(Args ...args) ++ { ++ Shadow shadow = &call; ++ long id = (long)shadow; ++ auto iter = stub_wrappers.find(id); ++ assert(stub_wrappers.find(id) != stub_wrappers.end()); ++ LamdaWrapper<Lamda> *wrapper = dynamic_cast<LamdaWrapper<Lamda> *>(iter->second); ++ return LamdaCaller<LAMDA_FUNCTION_TYPE>::call(wrapper, args...); ++ } ++}; ++ ++template<typename Ret, class Obj,typename... Args, class Lamda> ++struct FuncShadow<Ret (Obj::*)(Args...), Lamda> ++{ ++ typedef Ret (*Shadow)(Obj *,Args...); ++ typedef Ret RetType; ++ static Ret call(Obj *obj, Args ...args) ++ { ++ Shadow shadow = &call; ++ long id = (long)shadow; ++ auto iter = stub_wrappers.find(id); ++ assert(stub_wrappers.find(id) != stub_wrappers.end()); ++ LamdaWrapper<Lamda> *wrapper = dynamic_cast<LamdaWrapper<Lamda> *>(iter->second); ++ return LamdaCaller<LAMDA_FUNCTION_TYPE>::call(wrapper, obj, args...); ++ } ++}; ++ ++ ++template<typename Ret, class Obj,typename... Args, class Lamda> ++struct FuncShadow<Ret (Obj::*)(Args...) const, Lamda> ++{ ++ typedef Ret (*Shadow)(Obj *,Args...); ++ typedef Ret RetType; ++ static Ret call(Obj *obj, Args ...args) ++ { ++ Shadow shadow = &call; ++ long id = (long)shadow; ++ auto iter = stub_wrappers.find(id); ++ assert(stub_wrappers.find(id) != stub_wrappers.end()); ++ LamdaWrapper<Lamda> *wrapper = dynamic_cast<LamdaWrapper<Lamda> *>(iter->second); ++ return LamdaCaller<LAMDA_FUNCTION_TYPE>::call(wrapper, obj, args...); ++ } ++}; ++ ++template<typename Func, class Lamda> ++typename FuncShadow<Func, Lamda>::Shadow depictShadow(Wrapper **wrapper, Func func, Lamda lamda) ++{ ++ *wrapper = new LamdaWrapper<Lamda>(lamda); ++ typename FuncShadow<Func,Lamda>::Shadow shadow = &FuncShadow<Func,Lamda>::call; ++ long id = (long)shadow; ++ assert(stub_wrappers.find(id) == stub_wrappers.end()); ++ stub_wrappers.insert(std::make_pair(id,*wrapper)); ++ return shadow; ++} ++ ++void freeWrapper(Wrapper *wrapper); ++ ++} ++ ++#endif // STUBSHADOW_H +diff --git a/tests/kt-gtest/kt-test-utils/stub-ext/stubext.h b/tests/kt-gtest/kt-test-utils/stub-ext/stubext.h +new file mode 100755 +index 0000000..2a70a78 +--- /dev/null ++++ b/tests/kt-gtest/kt-test-utils/stub-ext/stubext.h +@@ -0,0 +1,129 @@ ++ ++#ifndef STUBEXT_H ++#define STUBEXT_H ++/* ++ * Author: Zhang Yu <clauszy@163.com> ++ * Maintainer: Zhang Yu <clauszy@163.com> ++ * ++ * MIT License ++ * ++ * Copyright (c) 2020 Zhang Yu ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in all ++ * copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++ * SOFTWARE. ++*/ ++//需修改Stub的私用成员函数和成员变量为保护类型 ++#include "stub.h" ++ ++#include "stub-shadow.h" ++ ++#ifdef DEBUG_STUB_INVOKE ++// use to make sure the stub function is invoked. ++# define __DBG_STUB_INVOKE__ printf("stub at %s:%d is invoked.\n", __FILE__, __LINE__); ++#else ++# define __DBG_STUB_INVOKE__ ++#endif ++ ++#define VADDR(CLASS_NAME, MEMBER_NAME) (typename stub_ext::VFLocator<decltype(&CLASS_NAME::MEMBER_NAME)>::Func)(&CLASS_NAME::MEMBER_NAME) ++ ++namespace stub_ext { ++ ++class StubExt : public Stub ++{ ++public: ++ StubExt() ++ : Stub() { } ++ ++ template<typename T, class Lamda> ++ bool set_lamda(T addr, Lamda lamda) ++ { ++ char *fn = addrof(addr); ++ if (m_result.find(fn) != m_result.end()) ++ reset(addr); ++ ++ Wrapper *wrapper = nullptr; ++ auto addr_stub = depictShadow(&wrapper, addr, lamda); ++ if (set(addr, addr_stub)) { ++ m_wrappers.insert(std::make_pair(fn, wrapper)); ++ return true; ++ } else { ++ freeWrapper(wrapper); ++ } ++ return false; ++ } ++ ++ template<typename T> ++ void reset(T addr) ++ { ++ Stub::reset(addr); ++ char *fn = addrof(addr); ++ auto iter = m_wrappers.find(fn); ++ if (iter != m_wrappers.end()) { ++ freeWrapper(iter->second); ++ m_wrappers.erase(iter); ++ } ++ } ++ ++ ~StubExt() ++ { ++ clear(); ++ } ++ ++ void clear() override ++ { ++ Stub::clear(); ++ for (auto iter = m_wrappers.begin(); iter != m_wrappers.end(); ++iter) { ++ freeWrapper(iter->second); ++ } ++ m_wrappers.clear(); ++ } ++ ++ template<class T> ++ static void *get_ctor_addr(bool start = true) ++ { ++ // the start vairable must be true, or the compiler will optimize out. ++ if (start) goto Start; ++ Call_Constructor: ++ // This line of code will not be executed. ++ // The purpose of the code is to allow the compiler to generate the assembly code that calls the constructor. ++ T(); ++ Start: ++ // The address of the line of code T() obtained by assembly ++ char *p = (char *)&&Call_Constructor; // https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html ++ // CALL rel32 ++ void *ret = 0; ++ char pos; ++ char call = 0xe8; ++ do { ++ pos = *p; ++ if (pos == call) { ++ ret = p + 5 + (*(int *)(p + 1)); ++ } ++ ++ } while (!ret && (++p)); ++ ++ return ret; ++ } ++ ++protected: ++ std::map<char *, Wrapper *> m_wrappers; ++}; ++ ++} ++ ++#endif // STUBEXT_H +diff --git a/tests/kt-gtest/kt-test.pro b/tests/kt-gtest/kt-test.pro +new file mode 100644 +index 0000000..6a1e7dd +--- /dev/null ++++ b/tests/kt-gtest/kt-test.pro +@@ -0,0 +1,8 @@ ++TEMPLATE = subdirs ++ ++SUBDIRS += \ ++ file-manager-operation \ ++ kt-desktop-gtest \ ++ kt-operation-gtest \ ++ kt-operation-menu-gtest \ ++ kt-peony-model-gtest +diff --git a/tests/kt-gtest/test-utils.pri b/tests/kt-gtest/test-utils.pri +new file mode 100644 +index 0000000..5569bad +--- /dev/null ++++ b/tests/kt-gtest/test-utils.pri +@@ -0,0 +1,21 @@ ++# test-utils.pri ++ ++CONFIG += console c++11 link_pkgconfig ++CONFIG += thread ++ ++TEST_UTILS_PATH = $$PWD/kt-test-utils ++INCLUDEPATH += $$TEST_UTILS_PATH/cpp-stub \ ++ $$TEST_UTILS_PATH/stub-ext \ ++ ++HEADERS += $$files($$TEST_UTILS_PATH/cpp-stub/*.h) \ ++ $$files($$TEST_UTILS_PATH/cpp-stub/*.hpp) \ ++ $$files($$TEST_UTILS_PATH/stub-ext/*.h) ++ ++SOURCES += $$files($$TEST_UTILS_PATH/cpp-stub/*.cpp) \ ++ $$files($$TEST_UTILS_PATH/stub-ext/*.cpp) ++ ++# 覆盖率 ++QMAKE_LFLAGS +=-fprofile-arcs -ftest-coverage ++QMAKE_CXXFLAGS += --coverage -fno-inline -fno-access-control -fno-exceptions ++ ++LIBS += -lgtest -lgcov +diff --git a/tests/tests.pro b/tests/tests.pro +new file mode 100644 +index 0000000..170f365 +--- /dev/null ++++ b/tests/tests.pro +@@ -0,0 +1,5 @@ ++TEMPLATE = subdirs ++ ++SUBDIRS += \ ++ kt-operation-test \ ++ kt-gtest +diff --git a/translations/libpeony-qt/libpeony-qt_vi.ts b/translations/libpeony-qt/libpeony-qt_vi.ts +new file mode 100644 +index 0000000..8757141 +--- /dev/null ++++ b/translations/libpeony-qt/libpeony-qt_vi.ts +@@ -0,0 +1,5539 @@ ++<?xml version="1.0" encoding="utf-8"?> ++<!DOCTYPE TS> ++<TS version="2.1" language="vi"> ++<context> ++ <name>ColorPushButton</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/color-pushbutton.cpp" line="52"/> ++ <source>label management ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/color-pushbutton.cpp" line="72"/> ++ <source>Remove "%1"</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>delete "%1"</source> ++ <translation type="vanished">删除“%1”</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/color-pushbutton.cpp" line="92"/> ++ <source>add "%1"</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>ConnectServerDialog</name> ++ <message> ++ <location filename="../../libpeony-qt/connect-server-dialog.ui" line="14"/> ++ <source>Connect to Sever</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-server-dialog.ui" line="32"/> ++ <source>Domain</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-server-dialog.ui" line="39"/> ++ <source>Password</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-server-dialog.ui" line="55"/> ++ <source>Save Password</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-server-dialog.ui" line="62"/> ++ <source>User</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-server-dialog.ui" line="82"/> ++ <source>Anonymous</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-server-dialog.cpp" line="35"/> ++ <source>Ok</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-server-dialog.cpp" line="36"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>DiscControl</name> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/disc/disccontrol.cpp" line="477"/> ++ <source> is busy!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/disc/disccontrol.cpp" line="516"/> ++ <source>is busy!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/disc/disccontrol.cpp" line="563"/> ++ <source> not support udf at present.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/disc/disccontrol.cpp" line="570"/> ++ <source>unmount disc failed before udf format.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>is not properly formatted.</source> ++ <translation type="vanished">格式不正确。</translation> ++ </message> ++ <message> ++ <source>Can not found newfs_udf tool.</source> ++ <translation type="vanished">未找到newfs_udf工具。</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/disc/disccontrol.cpp" line="723"/> ++ <source>DVD+RW udf format fail.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/disc/disccontrol.cpp" line="755"/> ++ <source>preparation failed before DVD-RW udf format.</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>FileLabelModel</name> ++ <message> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="38"/> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="56"/> ++ <source>Red</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="39"/> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="57"/> ++ <source>Orange</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="40"/> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="58"/> ++ <source>Yellow</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="41"/> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="59"/> ++ <source>Green</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="42"/> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="60"/> ++ <source>Blue</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="43"/> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="61"/> ++ <source>Purple</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Gray</source> ++ <translation type="vanished">灰色</translation> ++ </message> ++ <message> ++ <source>Transparent</source> ++ <translation type="vanished">无颜色</translation> ++ </message> ++ <message> ++ <source>Delete file Warning</source> ++ <translation type="vanished">删除文件警告</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="130"/> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="435"/> ++ <source>Error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="130"/> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="435"/> ++ <source>Label or color is duplicated.</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>FileOperationHelper</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-helper.cpp" line="157"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-helper.cpp" line="175"/> ++ <source>Burn failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Format_Dialog</name> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="20"/> ++ <source>Dialog</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="32"/> ++ <source>rom_size</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="45"/> ++ <source>system</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="59"/> ++ <source>vfat/fat32</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="64"/> ++ <source>exfat</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="69"/> ++ <source>ntfs</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>vfat</source> ++ <translation type="vanished">VFAT</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="74"/> ++ <source>ext4</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="88"/> ++ <source>device_name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="114"/> ++ <source>clean it total</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="127"/> ++ <source>ok</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="140"/> ++ <source>close</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.ui" line="179"/> ++ <source>TextLabel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>qmesg_notify</source> ++ <translation type="vanished">通知</translation> ++ </message> ++ <message> ++ <source>Format operation has been finished successfully.</source> ++ <translation type="vanished">格式化操作已成功完成。</translation> ++ </message> ++ <message> ++ <source>Sorry, the format operation is failed!</source> ++ <translation type="vanished">很遗憾,格式化操作失败了,您可以重新试下!</translation> ++ </message> ++ <message> ++ <source>Formatting this volume will erase all data on it. Please backup all retained data before formatting. Do you want to continue ?</source> ++ <translation type="vanished">格式化此卷将清除其上的所有数据。请在格式化之前备份所有保留的数据。您想继续吗?</translation> ++ </message> ++ <message> ++ <source>format</source> ++ <translation type="vanished">格式化</translation> ++ </message> ++ <message> ++ <source>begin format</source> ++ <translation type="vanished">开始</translation> ++ </message> ++ <message> ++ <source>format_success</source> ++ <translation type="vanished">格式化成功!</translation> ++ </message> ++ <message> ++ <source>format_err</source> ++ <translation type="vanished">格式化失败!</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="154"/> ++ <source>Format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="167"/> ++ <source>Rom size:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="173"/> ++ <source>Filesystem:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="183"/> ++ <source>Disk name:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="213"/> ++ <source>Completely erase(Time is longer, please confirm!)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="231"/> ++ <source>Set password</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="232"/> ++ <source>Set password for volume based on LUKS (only ext4)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="238"/> ++ <source>Formatting to the ext4 file system may cause other users to be unable to read or write to the USB drive</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="246"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="247"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="361"/> ++ <source>Data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="238"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="567"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="567"/> ++ <source>Device name cannot start with a decimal point, Please re-enter!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="578"/> ++ <source>Enter Password:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="596"/> ++ <source>Password too short, please retype a password more than 6 characters</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="695"/> ++ <source>%1/sec, %2 remaining.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="695"/> ++ <source>over one day</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="697"/> ++ <source>getting progress...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1290"/> ++ <source>Error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1290"/> ++ <source>Block not existed!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1337"/> ++ <source>Formatting. Do not close this window</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>KyFileDialogRename</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="97"/> ++ <source>Renaming "%1"</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="98"/> ++ <source>Renaming failed, the reason is: %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="98"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="107"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="116"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="121"/> ++ <source>Filename too long</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="102"/> ++ <source>Copying "%1"</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="106"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="115"/> ++ <source>To "%1"</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="107"/> ++ <source>Copying failed, the reason is: %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="111"/> ++ <source>Moving "%1"</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="116"/> ++ <source>Moving failed, the reason is: %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="120"/> ++ <source>File operation error:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="121"/> ++ <source>The reason is: %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="140"/> ++ <source>Truncation</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="141"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="213"/> ++ <source>Save</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="171"/> ++ <source>All applications</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="174"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="212"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="272"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="175"/> ++ <source>Apply</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="208"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="258"/> ++ <source>Bytes</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="264"/> ++ <source>Front truncation</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="265"/> ++ <source>Post truncation</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="321"/> ++ <source>Description: Skip copying files of the current type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="326"/> ++ <source>truncate interval</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="327"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="345"/> ++ <source>.</source> ++ <translation></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="328"/> ++ <source>Explanation: Truncate the portion of the file name that exceeds 225 bytes and select</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="337"/> ++ <source>Description: By default, save to "%1/扩展".</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="344"/> ++ <source>modify the name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="346"/> ++ <source>Explanation: When renaming a file name, ensure it is within 255 bytes and </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Explanation: When renaming a file name, ensure it is within 225 bytes and </source> ++ <translation type="vanished">说明:用户重命名文件名,保证在225字节以内,去</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="139"/> ++ <source>Skip</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Skip All</source> ++ <translation type="vanished">全部跳过</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="142"/> ++ <source>Rename</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Please enter a new name</source> ++ <translation type="vanished">请输入文件名</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="273"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>MainProgressBar</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="438"/> ++ <source>File operation</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="493"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.h" line="319"/> ++ <source>starting ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="457"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="598"/> ++ <source>cancel all file operations</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="447"/> ++ <source>Minimize</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="455"/> ++ <source>Close</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="458"/> ++ <source>Are you sure to cancel all file operations?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="599"/> ++ <source>Are you sure want to cancel all file operations</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="460"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="601"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="461"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="602"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="634"/> ++ <source>continue</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="636"/> ++ <source>pause</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="716"/> ++ <source>canceling ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="719"/> ++ <source>sync ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>MessageDialog</name> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1837"/> ++ <source>Peony</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1864"/> ++ <source>Forcibly pulling out the device may cause data ++ loss or device exceptions!</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>OtherButton</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="825"/> ++ <source>Other queue</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::AdvanceSearchBar</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="55"/> ++ <source>Key Words</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="58"/> ++ <source>input key words...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="59"/> ++ <source>Search Location</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="61"/> ++ <source>choose search path...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="68"/> ++ <source>browse</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="69"/> ++ <source>File Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="71"/> ++ <source>Choose File Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="76"/> ++ <source>Modify Time</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="78"/> ++ <source>Choose Modify Time</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="83"/> ++ <source>File Size</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="85"/> ++ <source>Choose file size</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="90"/> ++ <source>show hidden file</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="91"/> ++ <source>go back</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="92"/> ++ <source>hidden advance search page</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="94"/> ++ <source>file name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="95"/> ++ <source>content</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="100"/> ++ <source>search</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="101"/> ++ <source>start search</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="174"/> ++ <source>Select path</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="193"/> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="202"/> ++ <source>Operate Tips</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="194"/> ++ <source>Have no key words or search location!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.cpp" line="203"/> ++ <source>Search file name or content at least choose one!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Search content or file name at least choose one!</source> ++ <translation type="vanished">搜索文件名或者内容请至少指定一个!</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="57"/> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="59"/> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="60"/> ++ <source>all</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="57"/> ++ <source>file folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="57"/> ++ <source>image</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="57"/> ++ <source>video</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="58"/> ++ <source>text file</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="58"/> ++ <source>audio</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="58"/> ++ <source>others</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="58"/> ++ <source>wps file</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="59"/> ++ <source>today</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="59"/> ++ <source>this week</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="59"/> ++ <source>this month</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="59"/> ++ <source>this year</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="59"/> ++ <source>yesterday</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="59"/> ++ <source>last week</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="59"/> ++ <source>last month</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="59"/> ++ <source>last year</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>year ago</source> ++ <translation type="vanished">去年</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="60"/> ++ <source>tiny(0-16K)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="60"/> ++ <source>small(16k-1M)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="60"/> ++ <source>medium(1M-100M)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="60"/> ++ <source>big(100M-1G)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="60"/> ++ <source>large(>1G)</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::AdvancedLocationBar</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/advanced-location-bar.cpp" line="200"/> ++ <source>Search Content...</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::AdvancedPermissionsPage</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="683"/> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="901"/> ++ <source>Permission refinement settings</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="796"/> ++ <source>Permission refinement settings tip</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="796"/> ++ <source>Setting ACL permissions will result in a change in the user group permissions for basic permissions. Do you need to continue setting ACL permissions?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="828"/> ++ <source>User</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="828"/> ++ <source>Read</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="828"/> ++ <source>Write</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="828"/> ++ <source>Executable</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="913"/> ++ <source>delete</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="915"/> ++ <source>Inherit permission</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="930"/> ++ <source>Add</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="947"/> ++ <source>Apply</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="948"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::AllFileLaunchDialog</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="380"/> ++ <source>Choose new application</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="382"/> ++ <source>Choose an Application to open this file</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="389"/> ++ <source>apply now</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="395"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="396"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::AudioPlayManager</name> ++ <message> ++ <source>Operation file Warning</source> ++ <translation type="vanished">文件操作警告</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::BasicPropertiesPage</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="935"/> ++ <source>Choose a custom icon</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="271"/> ++ <source>Type:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Display Name:</source> ++ <translation type="vanished">名称:</translation> ++ </message> ++ <message> ++ <source>Location:</source> ++ <translation type="vanished">路径:</translation> ++ </message> ++ <message> ++ <source>Overview:</source> ++ <translation type="vanished">概览:</translation> ++ </message> ++ <message> ++ <source>Change</source> ++ <translation type="vanished">更改</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="243"/> ++ <source>Name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="244"/> ++ <source>Location</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>move</source> ++ <translation type="vanished">移动</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="451"/> ++ <source>symbolLink</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="455"/> ++ <source>Folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="278"/> ++ <source>Include:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="282"/> ++ <source>Open with:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="286"/> ++ <source>Description:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="289"/> ++ <source>Select multiple files</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="277"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="295"/> ++ <source>Size:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Total size:</source> ++ <translation type="vanished">实际大小:</translation> ++ </message> ++ <message> ++ <source>Space Useage:</source> ++ <translation type="vanished">占用空间:</translation> ++ </message> ++ <message> ++ <source>yyyy-MM-dd, HH:mm:ss</source> ++ <translation type="vanished">yyyy年MM月dd日, HH:mm:ss</translation> ++ </message> ++ <message> ++ <source>yyyy-MM-dd, hh:mm:ss AP</source> ++ <translation type="vanished">yyyy年MM月dd日, hh:mm:ss AP</translation> ++ </message> ++ <message> ++ <source>Time Created:</source> ++ <translation type="vanished">创建时间:</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="296"/> ++ <source>Space Usage:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="307"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="315"/> ++ <source>Time Create:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="308"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="316"/> ++ <source>Time Modified:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="309"/> ++ <source>Time Access:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="322"/> ++ <source>Readonly</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="323"/> ++ <source>Hidden</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="332"/> ++ <source>Property:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="614"/> ++ <source>usershare</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="748"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="989"/> ++ <source>%1 (%2 Bytes)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="943"/> ++ <source>Please select a image that is smaller than 1MB.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Choose a new folder:</source> ++ <translation type="vanished">选择一个新的文件夹:</translation> ++ </message> ++ <message> ++ <source>Error</source> ++ <translation type="vanished">错误</translation> ++ </message> ++ <message> ++ <source>cannot move a folder to itself !</source> ++ <translation type="vanished">不能移动一个文件夹到它内部!</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="982"/> ++ <source>%1 Bytes</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>%1 KB (%2 Bytes)</source> ++ <translation type="vanished">%1 KB (%2 字节)</translation> ++ </message> ++ <message> ++ <source>%1 MB (%2 Bytes)</source> ++ <translation type="vanished">%1 MB (%2 字节)</translation> ++ </message> ++ <message> ++ <source>%1 GB (%2 Bytes)</source> ++ <translation type="vanished">%1 GB (%2 字节)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1002"/> ++ <source>%1 files, %2 folders</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1118"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1120"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1125"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1127"/> ++ <source>Can't get remote file information</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>%1 files (include root files), %2 hidden</source> ++ <translation type="vanished">共%1个文件(包括顶层目录),有%2个隐藏文件</translation> ++ </message> ++ <message> ++ <source>%1 total</source> ++ <translation type="vanished">共%1</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::ComputerPropertiesPage</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="102"/> ++ <source>CPU Name:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="103"/> ++ <source>CPU Core:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="104"/> ++ <source>Memory Size:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="116"/> ++ <source>User Name: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="117"/> ++ <source>Desktop: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="125"/> ++ <source>You should mount this volume first</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="142"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="240"/> ++ <source>Name: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="142"/> ++ <source>File System</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="142"/> ++ <source>Data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="143"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="245"/> ++ <source>Total Space: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="144"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="246"/> ++ <source>Used Space: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="145"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="247"/> ++ <source>Free Space: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="146"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="249"/> ++ <source>Type: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="262"/> ++ <source>Kylin Burner</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="268"/> ++ <source>Open with: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="275"/> ++ <source>Unknown</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::ConnectServerDialog</name> ++ <message> ++ <source>connect to server</source> ++ <translation type="vanished">连接服务器</translation> ++ </message> ++ <message> ++ <source>ip</source> ++ <translation type="vanished">服务器</translation> ++ </message> ++ <message> ++ <source>port</source> ++ <translation type="vanished">端口</translation> ++ </message> ++ <message> ++ <source>type</source> ++ <translation type="vanished">类型</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="143"/> ++ <source>Connect to server</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="168"/> ++ <source>Ip</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="170"/> ++ <source>Port</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="171"/> ++ <source>Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="202"/> ++ <source>Personal Collection server:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="215"/> ++ <source>Add</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="216"/> ++ <source>Delete</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="217"/> ++ <source>Connect</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="296"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="387"/> ++ <source>Ip input error, please re-enter!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="300"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="391"/> ++ <source>Port input error, please re-enter!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>add</source> ++ <translation type="vanished">添加</translation> ++ </message> ++ <message> ++ <source>delete</source> ++ <translatorcomment>连接</translatorcomment> ++ <translation type="vanished">删除</translation> ++ </message> ++ <message> ++ <source>connect</source> ++ <translation type="vanished">连接</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="296"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="300"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="387"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="391"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>ip input error, please re-enter!</source> ++ <translation type="vanished">IP输入错误, 请重新输入!</translation> ++ </message> ++ <message> ++ <source>port input error, please re-enter!</source> ++ <translation type="vanished">端口号输入错误, 请重新输入!</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::ConnectServerLogin</name> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="487"/> ++ <source>The login user</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="496"/> ++ <source>Please enter the %1's user name and password of the server.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="503"/> ++ <source>User's identity</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="504"/> ++ <source>Guest</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="520"/> ++ <source>Name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="521"/> ++ <source>Password</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="542"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="543"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>guest</source> ++ <translation type="vanished">游客(匿名登录)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="505"/> ++ <source>Registered users</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>name</source> ++ <translation type="vanished">用户名</translation> ++ </message> ++ <message> ++ <source>password</source> ++ <translation type="vanished">密码</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="522"/> ++ <source>Remember the password</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>cancel</source> ++ <translation type="vanished">取消</translation> ++ </message> ++ <message> ++ <source>ok</source> ++ <translation type="vanished">连接</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::CreateLinkInternalPlugin</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="132"/> ++ <source>Create Link to Desktop</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="158"/> ++ <source>Create Link to...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="161"/> ++ <source>Choose a Directory to Create Link</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.h" line="68"/> ++ <source>Peony-Qt Create Link Extension</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.h" line="71"/> ++ <source>Create Link Menu Extension.</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::CreateSharedFileLinkMenuPlugin</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="271"/> ++ <source>Create Link to Desktop</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.h" line="161"/> ++ <source>Peony-Qt Share File menu Extension</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.h" line="164"/> ++ <source>Tag with Menu.</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::CreateTemplateOperation</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="76"/> ++ <source>NewFile</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="92"/> ++ <source>Create file</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="105"/> ++ <source>NewFolder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="124"/> ++ <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="158"/> ++ <source>Create file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::CustomErrorHandler</name> ++ <message> ++ <location filename="../../libpeony-qt/custom-error-handler.cpp" line="43"/> ++ <source>Is Error Handled?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/custom-error-handler.cpp" line="48"/> ++ <source>Error not be handled correctly</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DefaultAcitonWidget</name> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="531"/> ++ <source>No default app</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DefaultOpenWithWidget</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="439"/> ++ <source>No default app</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DefaultPreviewPage</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="75"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="216"/> ++ <source>Select the file you want to preview...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="207"/> ++ <source>Can not preview this file.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Can not preivew this file.</source> ++ <translation type="vanished">不能预览该文件</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DefaultPreviewPageFactory</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page-factory.h" line="50"/> ++ <source>Default Preview</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page-factory.h" line="53"/> ++ <source>This is the Default Preview of peony-qt</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DetailsPropertiesPage</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="181"/> ++ <source>Name:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="184"/> ++ <source>File type:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="200"/> ++ <source>Location:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="211"/> ++ <source>yyyy-MM-dd, HH:mm:ss</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="204"/> ++ <source>Create time:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="208"/> ++ <source>Modify time:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>yyyy-MM-dd, hh:mm:ss AP</source> ++ <translation type="vanished">yyyy年MM月dd日, hh:mm:ss AP</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="232"/> ++ <source>File size:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="239"/> ++ <source>Width:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="242"/> ++ <source>Height:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="250"/> ++ <source>Owner</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="251"/> ++ <source>Owner:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="253"/> ++ <source>Computer</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="254"/> ++ <source>Computer:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="309"/> ++ <source>%1 (this computer)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="316"/> ++ <source>Unknown</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="358"/> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="359"/> ++ <source>Can't get remote file information</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="368"/> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page.cpp" line="369"/> ++ <source>%1 px</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DirectoryView::IconView</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/directory-view/view/icon-view/icon-view.h" line="68"/> ++ <source>Icon View</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/directory-view/view/icon-view/icon-view.cpp" line="334"/> ++ <source>warn</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/directory-view/view/icon-view/icon-view.cpp" line="334"/> ++ <source>This operation is not supported.</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DirectoryView::IconView2</name> ++ <message> ++ <source>Icon View</source> ++ <translation type="vanished">图标视图</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DirectoryView::ListView</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/directory-view/view/list-view/list-view.h" line="63"/> ++ <source>List View</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/directory-view/view/list-view/list-view.cpp" line="605"/> ++ <source>warn</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/directory-view/view/list-view/list-view.cpp" line="605"/> ++ <source>This operation is not supported.</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DirectoryView::ListView2</name> ++ <message> ++ <source>List View</source> ++ <translation type="vanished">列表视图</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DirectoryViewFactoryManager</name> ++ <message> ++ <source>Icon View</source> ++ <translation type="vanished">图标视图</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DirectoryViewMenu</name> ++ <message> ++ <source>Open in &New Window</source> ++ <translation type="vanished">在新窗口中打开(&N)</translation> ++ </message> ++ <message> ++ <source>Open in New &Tab</source> ++ <translation type="vanished">在新标签页中打开(&T)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="360"/> ++ <source>Add to bookmark</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>&Open "%1"</source> ++ <translation type="vanished">打开“%1”(&O)</translation> ++ </message> ++ <message> ++ <source>Open "%1" in &New Window</source> ++ <translation type="vanished">在新窗口中打开“%1”(&N)</translation> ++ </message> ++ <message> ++ <source>Open "%1" in New &Tab</source> ++ <translation type="vanished">在新标签页中打开“%1”(&T)</translation> ++ </message> ++ <message> ++ <source>Open "%1" with...</source> ++ <translation type="vanished">选用其它应用打开“%1”...</translation> ++ </message> ++ <message> ++ <source>&More applications...</source> ++ <translation type="vanished">更多应用...(&M)</translation> ++ </message> ++ <message> ++ <source>&Open</source> ++ <translation type="vanished">打开(&O)</translation> ++ </message> ++ <message> ++ <source>Open &with...</source> ++ <translation type="vanished">打开方式(&W)...</translation> ++ </message> ++ <message> ++ <source>&Open %1 selected files</source> ++ <translation type="vanished">打开%1个选中文件(&O)</translation> ++ </message> ++ <message> ++ <source>&New...</source> ++ <translation type="vanished">新建...(&N)</translation> ++ </message> ++ <message> ++ <source>Empty &File</source> ++ <translation type="vanished">空文件(&E)</translation> ++ </message> ++ <message> ++ <source>&Folder</source> ++ <translation type="vanished">文件夹(&F)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="691"/> ++ <source>New Folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Icon View</source> ++ <translation type="vanished">图标视图</translation> ++ </message> ++ <message> ++ <source>List View</source> ++ <translation type="vanished">列表视图</translation> ++ </message> ++ <message> ++ <source>View Type...</source> ++ <translation type="vanished">视图类型...</translation> ++ </message> ++ <message> ++ <source>Sort By...</source> ++ <translation type="vanished">排序类型...</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="743"/> ++ <source>Name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="745"/> ++ <source>File Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="746"/> ++ <source>File Size</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>New...</source> ++ <translation type="vanished">新建...</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="322"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="413"/> ++ <source>Open in New Window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="332"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="423"/> ++ <source>Open in New Tab</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="380"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="435"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="499"/> ++ <source>Open</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="391"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="451"/> ++ <source>Open with...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="406"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="488"/> ++ <source>More applications...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="508"/> ++ <source>Open %1 selected files</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="556"/> ++ <source>New</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="675"/> ++ <source>Empty File</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="687"/> ++ <source>Folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="717"/> ++ <source>View Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="737"/> ++ <source>Sort By</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="744"/> ++ <source>Modified Date</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="747"/> ++ <source>Original Path</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Sort Order...</source> ++ <translation type="vanished">排序顺序...</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="780"/> ++ <source>Ascending Order</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1470"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1480"/> ++ <source>Peony-Qt Filesafe Menu Extension</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1512"/> ++ <source>MultiSelect</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="779"/> ++ <source>Descending Order</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Sort Preferences...</source> ++ <translation type="vanished">排序偏好...</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="798"/> ++ <source>Folder First</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="807"/> ++ <source>Chinese First</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="816"/> ++ <source>Show Hidden</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="851"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="859"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1039"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1313"/> ++ <source>Copy</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1373"/> ++ <source>File:"%1" is not exist, did you moved or deleted it?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Peony-Qt filesafe menu Extension</source> ++ <translation type="vanished">文件保护箱扩展</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1470"/> ++ <source>Peony File Labels Menu Extension</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>&Copy</source> ++ <translation type="vanished">复制(&C)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="889"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1318"/> ++ <source>Cut</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="916"/> ++ <source>Delete to trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="983"/> ++ <source>Paste</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1052"/> ++ <source>Refresh</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1063"/> ++ <source>Select All</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1107"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1168"/> ++ <source>Properties</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1209"/> ++ <source>format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1263"/> ++ <source>Restore</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="928"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1033"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1293"/> ++ <source>Delete</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="772"/> ++ <source>Sort Order</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="793"/> ++ <source>Sort Preferences</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1372"/> ++ <source>Error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>File:"%1 is not exist, did you moved or deleted it?</source> ++ <translation type="vanished">文件:"%1" 不存在,您是否已经移动或者删除了它?</translation> ++ </message> ++ <message> ++ <source>File original path not exist, are you deleted or moved it?</source> ++ <translation type="vanished">文件原始路径未找到,您是否已经移动或删除了它?</translation> ++ </message> ++ <message> ++ <source>Cu&t</source> ++ <translation type="vanished">剪切(&T)</translation> ++ </message> ++ <message> ++ <source>&Delete to trash</source> ++ <translation type="vanished">删除到回收站(&D)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="931"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="942"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="950"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="959"/> ++ <source>Delete forever</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="966"/> ++ <source>Rename</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Select &All</source> ++ <translation type="vanished">全选(&A)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1072"/> ++ <source>Reverse Select</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>P&roperties</source> ++ <translation type="vanished">属性(&R)</translation> ++ </message> ++ <message> ++ <source>&Delete</source> ++ <translation type="vanished">删除(&D)</translation> ++ </message> ++ <message> ++ <source>&Rename</source> ++ <translation type="vanished">重命名(&R)</translation> ++ </message> ++ <message> ++ <source>&Paste</source> ++ <translation type="vanished">粘贴(&P)</translation> ++ </message> ++ <message> ++ <source>&Refresh</source> ++ <translation type="vanished">刷新(&R)</translation> ++ </message> ++ <message> ++ <source>&Properties</source> ++ <translation type="vanished">属性(&P)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1236"/> ++ <source>&Clean the Trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Delete file Warning</source> ++ <translation type="vanished">删除文件警告</translation> ++ </message> ++ <message> ++ <source>Delete Permanently</source> ++ <translation type="vanished">永久删除</translation> ++ </message> ++ <message> ++ <source>Are you sure that you want to delete these files? Once you start a deletion, the files deleting will never be restored again.</source> ++ <translation type="vanished">您确定要删除这些文件吗?一旦开始删除,这些文件将不可再恢复。</translation> ++ </message> ++ <message> ++ <source>&Restore</source> ++ <translation type="vanished">还原(&R)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1326"/> ++ <source>Clean All</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1343"/> ++ <source>Open Parent Folder in New Window</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::DirectoryViewWidget</name> ++ <message> ++ <source>Directory View</source> ++ <translation type="vanished">文件视图</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::ExtensionsManagerWidget</name> ++ <message> ++ <location filename="../../libpeony-qt/extensions-manager-widget.cpp" line="64"/> ++ <source>Extensions Manager</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/extensions-manager-widget.cpp" line="68"/> ++ <source>Available extensions</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/extensions-manager-widget.cpp" line="70"/> ++ <source>Ok</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/extensions-manager-widget.cpp" line="71"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FMWindow</name> ++ <message> ++ <location filename="../../libpeony-qt/windows/fm-window.cpp" line="93"/> ++ <source>File Manager</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/fm-window.cpp" line="171"/> ++ <source>advanced search</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/fm-window.cpp" line="174"/> ++ <source>clear record</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/fm-window.cpp" line="279"/> ++ <source>Loaing... Press Esc to stop a loading.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/fm-window.cpp" line="395"/> ++ <source>Author: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019-2020, Tianjin KYLIN Information Technology Co., Ltd.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Author: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019-2020, Tianjin KYLIN Information Technology Co., Ltd.</source> ++ <translation type="vanished">作者: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++版权所有(C): 2019-2020,天津麒麟信息技术有限公司.</translation> ++ </message> ++ <message> ++ <source>Ctrl+H</source> ++ <comment>Show|Hidden</comment> ++ <translation type="vanished">Ctrl+H</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/fm-window.cpp" line="324"/> ++ <source>Undo</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/fm-window.cpp" line="331"/> ++ <source>Redo</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/fm-window.cpp" line="394"/> ++ <source>Peony Qt</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Authour: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019-2020, Tianjin KYLIN Information Technology Co., Ltd.</source> ++ <translation type="vanished">作者: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++版权所有(C): 2019-2020,天津麒麟信息技术有限公司.</translation> ++ </message> ++ <message> ++ <source>Authour: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> ++ <translation type="vanished">作者: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++版权所有(C): 2019,天津麒麟信息技术有限公司.</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/fm-window.cpp" line="448"/> ++ <source>New Folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileBatchRenameOperation</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-batch-rename-operation.cpp" line="75"/> ++ <source>File Rename error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-batch-rename-operation.cpp" line="76"/> ++ <source>Invalid file name %1%2%3 .</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-batch-rename-operation.cpp" line="91"/> ++ <source>File Rename warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-batch-rename-operation.cpp" line="92"/> ++ <source>Are you sure to hidden these files?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-batch-rename-operation.cpp" line="209"/> ++ <location filename="../../libpeony-qt/file-operation/file-batch-rename-operation.cpp" line="246"/> ++ <source>Rename file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileCopy</name> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="174"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="182"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="202"/> ++ <source>Error in source or destination file path!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="191"/> ++ <source>Error when copy file: %1, can not copy special files, skip this file and continue?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="214"/> ++ <source>Can not copy %1, file doesn't exist. Has the file been renamed or moved?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="236"/> ++ <source>The dest file "%1" has existed!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="263"/> ++ <source>Vfat/FAT32 file systems do not support a single file that occupies more than 4 GB space!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="288"/> ++ <source>Error writing to file: Input/output error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="456"/> ++ <source>Failed to create %1. Please ensure if it is in root directory, or if the device supports gphoto2 protocol correctly.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="462"/> ++ <source>Failed to create %1.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Error opening source or destination file!</source> ++ <translation type="vanished">打开源文件或者目标文件出错!</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="391"/> ++ <source>Please check whether the device has been removed!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="393"/> ++ <source>Write file error: There is no available disk space for device!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Please confirm that the device controls are insufficient!</source> ++ <translation type="vanished">请确认设备空间是否足够!</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="450"/> ++ <source>File opening failure</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Reading and Writing files are inconsistent!</source> ++ <translation type="vanished">读和写文件不一致!</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="279"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="408"/> ++ <source>operation cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileCopyOperation</name> ++ <message> ++ <source>File copy</source> ++ <translation type="vanished">文件复制</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="231"/> ++ <source>Create folder %1 failed: %2</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="235"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="625"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1082"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1143"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1321"/> ++ <source>File copy error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="249"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="276"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="650"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="674"/> ++ <source>The file name exceeds the limit</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="633"/> ++ <source>Cannot opening file, permission denied!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="635"/> ++ <source>File:%1 was not found.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1069"/> ++ <source>File System</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1071"/> ++ <source>Data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1077"/> ++ <source>%1 no space left on device. Copy file size: %2 GB, Space needed: %3 GB.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1189"/> ++ <source>Link file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1323"/> ++ <source>Burning does not support replacement</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Burn failed</source> ++ <translation type="vanished">刻录失败</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileDeleteOperation</name> ++ <message> ++ <source>File delete</source> ++ <translation type="vanished">文件删除</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-delete-operation.cpp" line="84"/> ++ <location filename="../../libpeony-qt/file-operation/file-delete-operation.cpp" line="110"/> ++ <source>File delete error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-delete-operation.cpp" line="147"/> ++ <source>Delete file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-delete-operation.cpp" line="150"/> ++ <source>Invalid Operation! Can not delete "%1".</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileEnumerator</name> ++ <message> ++ <source>Delete file Warning</source> ++ <translation type="vanished">删除文件警告</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-enumerator.cpp" line="585"/> ++ <source>The password dialog box is canceled</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-enumerator.cpp" line="587"/> ++ <source>Message recipient disconnected from message bus without replying!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-enumerator.cpp" line="589"/> ++ <source>Error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Did not find target path, do you move or deleted it?</source> ++ <translation type="vanished">未找到目标路径,您是否已经移动或删除了它?</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileInfo</name> ++ <message> ++ <location filename="../../libpeony-qt/file-info.cpp" line="309"/> ++ <source>data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-info.cpp" line="444"/> ++ <source>folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-info.cpp" line="448"/> ++ <location filename="../../libpeony-qt/file-info.cpp" line="456"/> ++ <location filename="../../libpeony-qt/file-info.cpp" line="458"/> ++ <source>file</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-info.cpp" line="451"/> ++ <source>text file</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileInfoJob</name> ++ <message> ++ <source>Trash</source> ++ <translation type="vanished">回收站</translation> ++ </message> ++ <message> ++ <source>Computer</source> ++ <translation type="vanished">计算机</translation> ++ </message> ++ <message> ++ <source>Network</source> ++ <translation type="vanished">网络</translation> ++ </message> ++ <message> ++ <source>Recent</source> ++ <translation type="vanished">最近</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileInformationLabel</name> ++ <message> ++ <source>File location:</source> ++ <translation type="vanished">文件位置:</translation> ++ </message> ++ <message> ++ <source>File size:</source> ++ <translation type="vanished">文件大小:</translation> ++ </message> ++ <message> ++ <source>Modify time:</source> ++ <translation type="vanished">修改时间:</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileInfosJob</name> ++ <message> ++ <source>Computer</source> ++ <translation type="vanished">计算机</translation> ++ </message> ++ <message> ++ <source>Network</source> ++ <translation type="vanished">网络</translation> ++ </message> ++ <message> ++ <source>Recent</source> ++ <translation type="vanished">最近</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileItem</name> ++ <message> ++ <source>Delete file Warning</source> ++ <translation type="vanished">删除文件警告</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="251"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="253"/> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="326"/> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="338"/> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="346"/> ++ <source>Error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="313"/> ++ <source>Open Link failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="314"/> ++ <source>File not exist, do you want to delete the link file?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="327"/> ++ <source>Can not open path "%1",permission denied.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="337"/> ++ <source>Can not find path "%1",are you moved or renamed it?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Can not find path "%1" .</source> ++ <translation type="vanished">找不到路径: "%1" 。</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileItemModel</name> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="357"/> ++ <source>child(ren)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="344"/> ++ <source>Symbol Link, </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="403"/> ++ <source>File Name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="407"/> ++ <source>Delete Date</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="409"/> ++ <source>Create Date</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="416"/> ++ <source>File Size</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="418"/> ++ <source>Original Path</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="414"/> ++ <source>File Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="412"/> ++ <source>Modified Date</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileLabelInternalMenuPlugin</name> ++ <message> ++ <source>Add File Label...</source> ++ <translation type="vanished">添加标记...</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="202"/> ++ <source>Add File Label</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="225"/> ++ <source>Delete All Label</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.h" line="104"/> ++ <source>Peony File Labels Menu Extension</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.h" line="107"/> ++ <source>Tag a File with Menu.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="237"/> ++ <source>label management ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileLabelWidget</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="422"/> ++ <source>label management ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileLauchDialog</name> ++ <message> ++ <source>Applications</source> ++ <translation type="vanished">应用程序</translation> ++ </message> ++ <message> ++ <source>Choose an Application to open this file</source> ++ <translation type="vanished">选择一个应用打开这个文件</translation> ++ </message> ++ <message> ++ <source>Set as Default</source> ++ <translation type="vanished">设为默认</translation> ++ </message> ++ <message> ++ <source>OK</source> ++ <translation type="vanished">确定</translation> ++ </message> ++ <message> ++ <source>No application is set to open file %1</source> ++ <translation type="vanished">未设定用来打开文件“%1”的应用程序。</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="146"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="162"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="177"/> ++ <source>The opening mode of the %1 %2</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="146"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="147"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="177"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="178"/> ++ <source>unknown</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="147"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="178"/> ++ <source>No application is set to open file "%1 %2"</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="152"/> ++ <source>Still using the last opened application:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="162"/> ++ <source>known</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="165"/> ++ <source>Open application is used by default:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="186"/> ++ <source>You can search in the Software Center for an application that can open this file, or select an existing application on your computer.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="208"/> ++ <source>Other application:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="210"/> ++ <source>Select application:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="256"/> ++ <source>Always open the %1%2 file with this application</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="279"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="286"/> ++ <source>Choose other application</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="280"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="293"/> ++ <source>Go to application center</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="351"/> ++ <source>Ok</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="352"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="421"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="412"/> ++ <source>Desktop files(*.desktop)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="419"/> ++ <source>Select Open Action</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="420"/> ++ <source>Select</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileLaunchAction</name> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="215"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="310"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="498"/> ++ <source>Execute Directly</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="216"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="311"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="499"/> ++ <source>Execute in Terminal</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="219"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="315"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="503"/> ++ <source>Detected launching an executable file %1, you want?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Delete file Warning</source> ++ <translation type="vanished">删除文件警告</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="256"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="368"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="537"/> ++ <source>Open Failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="256"/> ++ <source>Can not open %1, file not exist, is it deleted?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>File not exist, is it deleted or moved to other path?</source> ++ <translation type="vanished">文件不存在,您是否已将其删除或挪动位置?</translation> ++ </message> ++ <message> ++ <source>Can not open %1</source> ++ <translation type="vanished">不能打开%1</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="189"/> ++ <source>No Permission</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="189"/> ++ <source>File is not readable. Please check if file has read permisson.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="309"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="497"/> ++ <source>By Default App</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="314"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="502"/> ++ <source>Launch Options</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="357"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="526"/> ++ <source>Open Link failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="358"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="527"/> ++ <source>File not exist, do you want to delete the link file?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="369"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="538"/> ++ <source>Can not open %1, Please confirm you have the right authority.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="373"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="543"/> ++ <source>Open App failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="374"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="544"/> ++ <source>The linked app is changed or uninstalled, so it can not work correctly. ++Do you want to delete the link file?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="388"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="393"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="555"/> ++ <source>Error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="389"/> ++ <source>File original path not exist, are you deleted or moved it?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="393"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="555"/> ++ <source>Can not get a default application for opening %1, do you want open it with text format?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Warning</source> ++ <translation type="vanished">警告</translation> ++ </message> ++ <message> ++ <source>Can not open the file, application is disabled</source> ++ <translation type="vanished">无法打开文件,应用被禁用。</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileLinkOperation</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-link-operation.cpp" line="46"/> ++ <location filename="../../libpeony-qt/file-operation/file-link-operation.cpp" line="49"/> ++ <source>Symbolic Link</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-link-operation.cpp" line="89"/> ++ <source>Link file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Link file</source> ++ <translation type="vanished">创建文件链接</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileMoveOperation</name> ++ <message> ++ <source>Invalid move operation, cannot move a file itself.</source> ++ <translation type="vanished">非法的移动操作,不能自移动到自身。</translation> ++ </message> ++ <message> ++ <source>Move file</source> ++ <translation type="vanished">文件移动</translation> ++ </message> ++ <message> ++ <source>Create file</source> ++ <translation type="vanished">文件创建</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="186"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="415"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="535"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="867"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1776"/> ++ <source>Move file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="302"/> ++ <source>File System</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="304"/> ++ <source>Data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="310"/> ++ <source>%1 no space left on device. Copy file size: %2 GB, Space needed: %3 GB.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="315"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1952"/> ++ <source>File move error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="831"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="841"/> ++ <source>Invalid move operation, cannot move a file into its sub directories.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="885"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="916"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="969"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1239"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1263"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1396"/> ++ <source>The file name exceeds the limit</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1216"/> ++ <source>Create file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1222"/> ++ <source>Cannot opening file, permission denied!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1224"/> ++ <source>File:%1 was not found.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="832"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1683"/> ++ <source>Invalid Operation.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1743"/> ++ <source>File delete error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1822"/> ++ <source>Link file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1954"/> ++ <source>Burning does not support replacement</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Burn failed</source> ++ <translation type="vanished">刻录失败</translation> ++ </message> ++ <message> ++ <source>File delete</source> ++ <translation type="vanished">文件删除</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="839"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1745"/> ++ <source>Invalid Operation</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationAfterProgressPage</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="362"/> ++ <source>&More Details</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationErrorDialog</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="45"/> ++ <source>File Operation Error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="53"/> ++ <source>unkwon</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="54"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="55"/> ++ <source>null</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="57"/> ++ <source>Error message:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="58"/> ++ <source>Source File:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="59"/> ++ <source>Dest File:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="63"/> ++ <source>Ignore</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="64"/> ++ <source>Ignore All</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="65"/> ++ <source>Overwrite</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="66"/> ++ <source>Overwrite All</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="67"/> ++ <source>Backup</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="68"/> ++ <source>Backup All</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="69"/> ++ <source>&Retry</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog.cpp" line="70"/> ++ <source>&Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationErrorDialogBase</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialog-base.cpp" line="82"/> ++ <source>Close</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationErrorDialogConflict</name> ++ <message> ++ <source>This location already contains a file with the same name.</source> ++ <translation type="vanished">目标文件夹里已经包含有同名文件</translation> ++ </message> ++ <message> ++ <source>Please select the file to keep</source> ++ <translation type="vanished">请选择要保留的文件</translation> ++ </message> ++ <message> ++ <source>This location already contains the file,</source> ++ <translation type="vanished">这里已包含此文件</translation> ++ </message> ++ <message> ++ <source>Do you want to override it?</source> ++ <translation type="vanished">你确定要覆盖它吗</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="54"/> ++ <source>Replace</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="63"/> ++ <source>Ignore</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="82"/> ++ <source>Do the same</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="105"/> ++ <source><p>This location already contains the file '%1', Do you want to override it?</p></source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="111"/> ++ <source>Unexpected error from %1 to %2</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Then do the same thing in a similar situation</source> ++ <translation type="vanished">之后类似情况执行相同操作</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="72"/> ++ <source>Backup</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Cancel</source> ++ <translation type="vanished">取消</translation> ++ </message> ++ <message> ++ <source>OK</source> ++ <translation type="vanished">确定</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationErrorDialogNotSupported</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="320"/> ++ <source>Yes</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="312"/> ++ <source>No</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Cancel</source> ++ <translation type="vanished">取消</translation> ++ </message> ++ <message> ++ <source>Do the same</source> ++ <translation type="vanished">全部应用</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="357"/> ++ <source>Make sure the disk is not full or write protected and that the file is not protected</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationErrorDialogWarning</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="204"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="213"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="245"/> ++ <source>Make sure the disk is not full or write protected and that the file is not protected</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Please make sure the disk is not full or not is write protected, or file is not being used.</source> ++ <translation type="vanished">请确保磁盘未满或未被写保护或未被使用。</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationInfo</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="1096"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="1098"/> ++ <source>Symbolic Link</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source> - Symbolic Link</source> ++ <translation type="vanished">-快捷方式</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationManager</name> ++ <message> ++ <source>Delete file Warning</source> ++ <translation type="vanished">删除文件警告</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="262"/> ++ <source>Warn</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="262"/> ++ <source>'%1' is occupied,you cannot operate!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>No, go to settings</source> ++ <translation type="vanished">否,跳转到设置</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="282"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="286"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="291"/> ++ <source>Do you want to put selected %1 item(s) into trash?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="293"/> ++ <source>Do not show again</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="461"/> ++ <source>File System</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="463"/> ++ <source>Data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="467"/> ++ <source>Insufficient storage space</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="470"/> ++ <source>%1 no space left on device. Copy file size: %2 GB, Space needed: %3 GB.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="512"/> ++ <source>Can't delete.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="513"/> ++ <source>You can't delete a file whenthe file is doing another operation</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="634"/> ++ <source>File Operation is Busy</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="635"/> ++ <source>There have been one or more fileoperation(s) executing before. Youroperation will wait for executinguntil it/them done. If you really want to execute file operations parallelly anyway, you can change the default option "Allow Parallel" in option menu.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="661"/> ++ <source>The long name file is saved to %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>The system cannot hibernate or sleep</source> ++ <translation type="vanished">无法进入休眠或睡眠模式</translation> ++ </message> ++ <message> ++ <source>The file operation is in progress. Ensure that the file operation is complete or canceled before hibernating or sleeping</source> ++ <translation type="vanished">文件操作进行中,\ ++进入休眠或者睡眠之前,请先确保文件操作已完成或者取消</translation> ++ </message> ++ <message> ++ <source>There have been one or more fileoperation(s) executing before. Youroperation will wait for executinguntil it/them done.</source> ++ <translation type="vanished">在执行该操作之前有操作未完成,它需要等待上一个操作完成后再执行。</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationPreparePage</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="298"/> ++ <source>counting:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="299"/> ++ <source>state:</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationProgressPage</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="321"/> ++ <source>&More Details</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="332"/> ++ <source>From:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="333"/> ++ <source>To:</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileOperationProgressWizard</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="55"/> ++ <source>File Manager</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="59"/> ++ <source>&Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="68"/> ++ <source>Preparing...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="71"/> ++ <source>Handling...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="74"/> ++ <source>Clearing...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="77"/> ++ <source>Rollbacking...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="81"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="94"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="120"/> ++ <source>File Operation</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="95"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="121"/> ++ <source>A file operation is running backend...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="160"/> ++ <source>%1 files, %2</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="260"/> ++ <source>%1 done, %2 total, %3 of %4.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="203"/> ++ <source>clearing: %1, %2 of %3</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="248"/> ++ <source>copying...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-wizard.cpp" line="278"/> ++ <source>Syncing...</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FilePreviewPage</name> ++ <message> ++ <source>File Name:</source> ++ <translation type="vanished">文件名称:</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="258"/> ++ <source>File Type:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="286"/> ++ <source>Time Access:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="279"/> ++ <source>Time Modified:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="295"/> ++ <source>Children Count:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="265"/> ++ <source>Size:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="272"/> ++ <source>Time Created:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="304"/> ++ <source>Image resolution:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="311"/> ++ <source>color model:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="380"/> ++ <source>usershare</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Image size:</source> ++ <translation type="vanished">图片尺寸:</translation> ++ </message> ++ <message> ++ <source>Image format:</source> ++ <translation type="vanished">图片格式:</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="435"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="436"/> ++ <source>%1x%2</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="493"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="494"/> ++ <source>%1 total, %2 hidden</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileRenameDialog</name> ++ <message> ++ <source>Names automatically add serial Numbers (e.g., 1,2,3...)</source> ++ <translation type="vanished">名称后自动添加序号(如:1,2,3...)</translation> ++ </message> ++ <message> ++ <source>Cancel</source> ++ <translation type="vanished">取消</translation> ++ </message> ++ <message> ++ <source>New file name</source> ++ <translation type="vanished">文件名</translation> ++ </message> ++ <message> ++ <source>Please enter the file name</source> ++ <translation type="vanished">请输入文件名</translation> ++ </message> ++ <message> ++ <source>OK</source> ++ <translation type="vanished">确定</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileRenameOperation</name> ++ <message> ++ <source>Rename file</source> ++ <translation type="vanished">文件重命名</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="76"/> ++ <source>File Rename error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="77"/> ++ <source>Invalid file name %1%2%3 .</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="93"/> ++ <source>Are you sure to hidden this file?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="126"/> ++ <source>When change the file suffix, the file may be invalid. Are you sure to change it ?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>The file %1%2%3 will be hidden when you refresh or change directory!</source> ++ <translation type="vanished">文件 %1%2%3 在刷新或者切换路径后将会被隐藏!</translation> ++ </message> ++ <message> ++ <source>The file %1%2%3 will be hidden when you refresh or rsort!</source> ++ <translation type="vanished">文件 %1%2%3 在刷新或者排序后将会被隐藏!</translation> ++ </message> ++ <message> ++ <source>The file %1%2%3 will be hidden!</source> ++ <translation type="vanished">文件%1%2%3将被隐藏!</translation> ++ </message> ++ <message> ++ <source>Invalid file name "%1" </source> ++ <translation type="vanished">文件名 "%1" 不合法</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="92"/> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="125"/> ++ <source>File Rename warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>The file "%1" will be hidden!</source> ++ <translation type="vanished">文件 "%1" 将会被隐藏!</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="217"/> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="249"/> ++ <source>Rename file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileTrashOperation</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="72"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="95"/> ++ <source>trash:///</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="75"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="98"/> ++ <source>Trash file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="78"/> ++ <source>Invalid Operation! Can not trash "%1".</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Can not trash</source> ++ <translation type="vanished">不能回收</translation> ++ </message> ++ <message> ++ <source>Can not trash files more than 10GB, would you like to delete it permanently?</source> ++ <translation type="vanished">无法回收大于10G的文件,是否需要永久删除?</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="202"/> ++ <source>An unmanageable conflict exists. Please check the recycle bin.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>The user does not have read and write rights to the file '%1' and cannot delete it to the Recycle Bin.</source> ++ <translation type="vanished">用户对当前文件 %1 没有读写权限,无法删除到回收站。</translation> ++ </message> ++ <message> ++ <source>Can not trash this file, would you like to delete it permanently?</source> ++ <translation type="vanished">不能回收该文件, 是否要永久删除?</translation> ++ </message> ++ <message> ++ <source>Can not trash %1, would you like to delete this file permanently?</source> ++ <translation type="vanished">不能回收%1, 是否永久删除?</translation> ++ </message> ++ <message> ++ <source>. Are you sure you want to permanently delete the file</source> ++ <translation type="vanished">,你确定要永久删除文件吗?</translation> ++ </message> ++ <message> ++ <source>The user does not have read and write rights to the file '%s' and cannot delete it to the Recycle Bin.</source> ++ <translation type="vanished">用户对当前文件 %s 没有读写权限,无法删除到回收站。</translation> ++ </message> ++ <message> ++ <source>Trash file</source> ++ <translation type="vanished">删除文件到回收站</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::FileUntrashOperation</name> ++ <message> ++ <source>Untrash file</source> ++ <translation type="vanished">撤销删除的文件</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-untrash-operation.cpp" line="159"/> ++ <source>Untrash file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::GlobalSettings</name> ++ <message> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="104"/> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="554"/> ++ <source>yyyy/MM/dd</source> ++ <translation></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="105"/> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="546"/> ++ <source>HH:mm:ss</source> ++ <translation></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="543"/> ++ <source>AP hh:mm:ss</source> ++ <translation></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="557"/> ++ <source>yyyy-MM-dd</source> ++ <translation></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::HeaderBar</name> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="954"/> ++ <source>Spread</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="955"/> ++ <source>Minimize</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="956"/> ++ <source>Close</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::LabelSettings</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="115"/> ++ <source>Name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="116"/> ++ <source>SideBar</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="117"/> ++ <source>Menu</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="124"/> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="178"/> ++ <source>Create New Label</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="126"/> ++ <source>Delete Label</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="136"/> ++ <source>Display the following items in the identification area: (maximum of 6)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="153"/> ++ <source>Rename</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="160"/> ++ <source>Edit Color</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="175"/> ++ <source>Delete This Label</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::LocationBar</name> ++ <message> ++ <source>click the blank area for edit</source> ++ <translation type="vanished">点击空白区域编辑路径</translation> ++ </message> ++ <message> ++ <source>Computer</source> ++ <translation type="obsolete">计算机</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="407"/> ++ <source>Search "%1" in "%2"</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="417"/> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="446"/> ++ <source>File System</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="417"/> ++ <source>Search results for all files marked in "%1" in "%2"</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>&Copy Directory</source> ++ <translation type="vanished">拷贝路径(&C)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="538"/> ++ <source>Open In New Tab</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="542"/> ++ <source>Open In New Window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Open In New &Tab</source> ++ <translation type="vanished">在新标签页中打开(&T)</translation> ++ </message> ++ <message> ++ <source>Open In &New Window</source> ++ <translation type="vanished">在新窗口中打开(&N)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="536"/> ++ <source>Copy Directory</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::MountOperation</name> ++ <message> ++ <location filename="../../libpeony-qt/mount-operation.cpp" line="93"/> ++ <source>Operation Cancelled</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/mount-operation.cpp" line="190"/> ++ <source>Login failed, unknown username or password error, please re-enter!</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::NavigationToolBar</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/navigation-tool-bar.cpp" line="35"/> ++ <source>Go Back</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/navigation-tool-bar.cpp" line="39"/> ++ <source>Go Forward</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/navigation-tool-bar.cpp" line="43"/> ++ <source>History</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/navigation-tool-bar.cpp" line="73"/> ++ <source>Clear History</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/navigation-tool-bar.cpp" line="88"/> ++ <source>Cd Up</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/navigation-tool-bar.cpp" line="94"/> ++ <source>Refresh</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::NewFileLaunchDialog</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="246"/> ++ <source>Choose new application</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="248"/> ++ <source>Choose an Application to open this file</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="255"/> ++ <source>apply now</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="261"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="262"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::OpenWithPropertiesPage</name> ++ <message> ++ <source>How do you want to open %1%2 files ?</source> ++ <translation type="vanished">您希望以什么方式打开 %1%2 文件?</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="111"/> ++ <source>How do you want to open "%1%2" files ?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="116"/> ++ <source>Default open with:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="135"/> ++ <source>Other:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="174"/> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="181"/> ++ <source>Choose other application</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="175"/> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="188"/> ++ <source>Go to application center</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::PathEdit</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/path-bar/path-edit.cpp" line="60"/> ++ <source>Go To</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::PermissionsPropertiesPage</name> ++ <message> ++ <source>User or Group</source> ++ <translation type="vanished">用户或组</translation> ++ </message> ++ <message> ++ <source>Type</source> ++ <translation type="vanished">类型</translation> ++ </message> ++ <message> ++ <source>Readable</source> ++ <translation type="vanished">可读</translation> ++ </message> ++ <message> ++ <source>Writeable</source> ++ <translation type="vanished">可写</translation> ++ </message> ++ <message> ++ <source>Excuteable</source> ++ <translation type="vanished">可执行</translation> ++ </message> ++ <message> ++ <source>File: %1</source> ++ <translation type="vanished">文件:%1</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="84"/> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="168"/> ++ <source>Target: %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="146"/> ++ <source>Read and Write</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="146"/> ++ <source>Readonly</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="146"/> ++ <source>Group or User</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="260"/> ++ <source>(Current User)</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="343"/> ++ <source>Current User</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="559"/> ++ <source>Permissions modify tip</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="559"/> ++ <source>The current file or folder has already set ACL permissions. Modifying user group permissions may cause the permissions set in ACL to be unusable. Do you want to continue modifying user group permissions?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="577"/> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="598"/> ++ <source>Permission refinement settings</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="599"/> ++ <source>The current user has set advanced sharing. If you still need to modify permissions, advanced sharing may not be available. Do you want to continue setting?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Read</source> ++ <translation type="vanished">可读</translation> ++ </message> ++ <message> ++ <source>Write</source> ++ <translation type="vanished">可写</translation> ++ </message> ++ <message> ++ <source>Executable</source> ++ <translation type="vanished">可执行</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="190"/> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="199"/> ++ <source>Can not get the permission info.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>(Me)</source> ++ <translation type="vanished">(我)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="331"/> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="333"/> ++ <source>Others</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Owner</source> ++ <translation type="vanished">拥有者</translation> ++ </message> ++ <message> ++ <source>Group</source> ++ <translation type="vanished">用户组</translation> ++ </message> ++ <message> ++ <source>Other</source> ++ <translation type="vanished">其他</translation> ++ </message> ++ <message> ++ <source>Other Users</source> ++ <translation type="vanished">其它用户</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page.cpp" line="339"/> ++ <source>You can not change the access of this file.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Me</source> ++ <translation type="vanished">我</translation> ++ </message> ++ <message> ++ <source>User</source> ++ <translation type="vanished">用户</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::PropertiesWindow</name> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="345"/> ++ <source>Trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="349"/> ++ <source>Recent</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="357"/> ++ <source>Selected</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="357"/> ++ <source> %1 Files</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="363"/> ++ <source>usershare</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="374"/> ++ <source>Data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="381"/> ++ <source>Properties</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="481"/> ++ <source>Ok</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="482"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="490"/> ++ <source>Restore</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Close</source> ++ <translation type="vanished">关闭</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::RecentAndTrashPropertiesPage</name> ++ <message> ++ <source>Show confirm dialog while trashing: </source> ++ <translation type="vanished">删除到回收站时弹出确认框:</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp" line="121"/> ++ <source>Show confirm dialog while trashing.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp" line="152"/> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp" line="158"/> ++ <source>Origin Path: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp" line="193"/> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp" line="227"/> ++ <source>Deletion Date: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp" line="179"/> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp" line="242"/> ++ <source>Size: </source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp" line="236"/> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp" line="243"/> ++ <source>Original Location: </source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SearchBar</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar.cpp" line="47"/> ++ <source>Input the search key of files you would like to find.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar.cpp" line="83"/> ++ <source>Input search key...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar.cpp" line="121"/> ++ <source>advance search</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar.cpp" line="122"/> ++ <source>clear record</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SearchBarContainer</name> ++ <message> ++ <source>Choose File Type</source> ++ <translation type="vanished">选择文件类型</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.cpp" line="259"/> ++ <source>Clear</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="120"/> ++ <source>all</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="120"/> ++ <source>file folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="120"/> ++ <source>image</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="121"/> ++ <source>video</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="121"/> ++ <source>text file</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="121"/> ++ <source>audio</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="121"/> ++ <source>others</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="121"/> ++ <source>wps file</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SharedFileLinkOperation</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/shared-file-link-operation.cpp" line="44"/> ++ <location filename="../../libpeony-qt/file-operation/shared-file-link-operation.cpp" line="47"/> ++ <source>Symbolic Link</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/shared-file-link-operation.cpp" line="80"/> ++ <source>The dest file "%1" has existed!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/shared-file-link-operation.cpp" line="86"/> ++ <source>Link file error</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SideBarCloudItem</name> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-cloud-item.cpp" line="40"/> ++ <source>CloudStorage</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-cloud-item.cpp" line="55"/> ++ <source>CloudFile</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SideBarFavoriteItem</name> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-favorite-item.cpp" line="84"/> ++ <source>Trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-favorite-item.cpp" line="87"/> ++ <source>Recent</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-favorite-item.cpp" line="95"/> ++ <source>Quick access</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Favorite</source> ++ <translation type="vanished">快速访问</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-favorite-item.cpp" line="189"/> ++ <source>KmreData</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SideBarFileSystemItem</name> ++ <message> ++ <source>Computer</source> ++ <translation type="vanished">计算机</translation> ++ </message> ++ <message> ++ <source>File System</source> ++ <translation type="vanished">文件系统</translation> ++ </message> ++ <message> ++ <source>data</source> ++ <translation type="vanished">数据盘</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-file-system-item.cpp" line="168"/> ++ <source>Data</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SideBarMenu</name> ++ <message> ++ <source>&Properties</source> ++ <translation type="vanished">属性(&P)</translation> ++ </message> ++ <message> ++ <source>P&roperties</source> ++ <translation type="vanished">属性(&R)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="68"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="91"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="117"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="136"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="317"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="371"/> ++ <source>Properties</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="102"/> ++ <source>Delete Symbolic</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="188"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="365"/> ++ <source>Unmount</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="197"/> ++ <source>Eject</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="225"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="258"/> ++ <source>Format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="294"/> ++ <source>burndata</source> ++ <translation></translation> ++ </message> ++ <message> ++ <source>&Delete Symbolic</source> ++ <translation type="vanished">删除(&D)</translation> ++ </message> ++ <message> ++ <source>&Unmount</source> ++ <translation type="vanished">卸载(&U)</translation> ++ </message> ++ <message> ++ <source>&Eject</source> ++ <translation type="vanished">弹出(&E)</translation> ++ </message> ++ <message> ++ <source>format</source> ++ <translation type="vanished">格式化</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SideBarModel</name> ++ <message> ++ <source>Shared Data</source> ++ <translation type="vanished">共享数据</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-model.cpp" line="98"/> ++ <source>Network</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SideBarPersonalItem</name> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-personal-item.cpp" line="42"/> ++ <source>Personal</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SideBarSeparatorItem</name> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-separator-item.h" line="68"/> ++ <source>(No Sub Directory)</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::StatusBar</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/status-bar/status-bar.cpp" line="94"/> ++ <location filename="../../libpeony-qt/controls/status-bar/status-bar.cpp" line="100"/> ++ <source>; %1 folders</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/status-bar/status-bar.cpp" line="95"/> ++ <location filename="../../libpeony-qt/controls/status-bar/status-bar.cpp" line="102"/> ++ <source>; %1 files, %2 total</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/status-bar/status-bar.cpp" line="97"/> ++ <source>; %1 folder</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/status-bar/status-bar.cpp" line="98"/> ++ <source>; %1 file, %2</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/status-bar/status-bar.cpp" line="105"/> ++ <source>%1 selected</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::SyncThread</name> ++ <message> ++ <location filename="../../libpeony-qt/sync-thread.cpp" line="66"/> ++ <source>notify</source> ++ <translatorcomment>温馨提示</translatorcomment> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::TagManagement</name> ++ <message> ++ <source>General</source> ++ <translation type="vanished">通用</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tag-management.cpp" line="316"/> ++ <source>Mark</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Sidebar</source> ++ <translation type="vanished">边栏</translation> ++ </message> ++ <message> ++ <source>Advanced</source> ++ <translation type="vanished">高级</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::ToolBar</name> ++ <message> ++ <source>Open in new &Window</source> ++ <translation type="vanished">在新窗口中打开(&W)</translation> ++ </message> ++ <message> ++ <source>Open in &New window</source> ++ <translation type="vanished">在新窗口中打开(&N)</translation> ++ </message> ++ <message> ++ <source>Open in new &Tab</source> ++ <translation type="vanished">在新标签页中打开(&T)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="138"/> ++ <source>Sort Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="140"/> ++ <source>File Name</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="146"/> ++ <source>File Type</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="149"/> ++ <source>File Size</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="143"/> ++ <source>Modified Date</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="72"/> ++ <source>Open in New window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="74"/> ++ <source>Open in new Tab</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="160"/> ++ <source>Ascending</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="156"/> ++ <source>Descending</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="190"/> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="340"/> ++ <source>Copy</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="193"/> ++ <source>Paste</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="196"/> ++ <source>Cut</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="199"/> ++ <source>Trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="216"/> ++ <source>Clean Trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Delete file Warning</source> ++ <translation type="vanished">删除文件警告</translation> ++ </message> ++ <message> ++ <source>Delete Permanently</source> ++ <translation type="vanished">永久删除</translation> ++ </message> ++ <message> ++ <source>Are you sure that you want to delete these files? Once you start a deletion, the files deleting will never be restored again.</source> ++ <translation type="vanished">您确定要删除这些文件吗?一旦开始删除,这些文件将不可再恢复。</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="221"/> ++ <source>Restore</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="270"/> ++ <source>Options</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="273"/> ++ <source>Forbid Thumbnail</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="281"/> ++ <source>Show Hidden</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="288"/> ++ <source>Resident in Backend</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="289"/> ++ <source>Let the program still run after closing the last window. This will reduce the time for the next launch, but it will also consume resources in backend.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="301"/> ++ <source>&Help</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="307"/> ++ <source>&About...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="309"/> ++ <source>Peony Qt</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/tool-bar.cpp" line="310"/> ++ <source>Author: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Author: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> ++ <translation type="vanished">作者: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++版权所有(C): 2019,天津麒麟信息技术有限公司.</translation> ++ </message> ++ <message> ++ <source>Authour: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> ++ <translation type="vanished">作者: ++ Yue Lan <lanyue@kylinos.cn> ++ Meihong He <hemeihong@kylinos.cn> ++ ++版权所有(C): 2019,天津麒麟信息技术有限公司.</translation> ++ </message> ++</context> ++<context> ++ <name>Peony::UserShareInfoManager</name> ++ <message> ++ <location filename="../../libpeony-qt/usershare-manager.cpp" line="115"/> ++ <location filename="../../libpeony-qt/usershare-manager.cpp" line="148"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>Peony::VolumeManager</name> ++ <message> ++ <location filename="../../libpeony-qt/volume-manager.cpp" line="160"/> ++ <source>Error</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>ProgressBar</name> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="860"/> ++ <source>starting ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="957"/> ++ <source>canceling ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="959"/> ++ <source>sync ...</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="1014"/> ++ <source>continue</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="1016"/> ++ <source>pause</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="1023"/> ++ <source>close</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>cancel file operation</source> ++ <translation type="vanished">取消文件操作</translation> ++ </message> ++ <message> ++ <source>Are you sure want to cancel the current selected file operation</source> ++ <translation type="vanished">你确定要取消当前选中的文件操作</translation> ++ </message> ++ <message> ++ <source>OK</source> ++ <translation type="vanished">确定</translation> ++ </message> ++ <message> ++ <source>Cancel</source> ++ <translation type="vanished">取消</translation> ++ </message> ++</context> ++<context> ++ <name>QObject</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/icon-view-factory.h" line="40"/> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/icon-view-factory.h" line="60"/> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/icon-view-factory.h" line="93"/> ++ <source>Icon View</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/icon-view-factory.h" line="46"/> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/icon-view-factory.h" line="99"/> ++ <source>Show the folder children as icons.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/list-view-factory.h" line="42"/> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/list-view-factory.h" line="62"/> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/list-view-factory.h" line="95"/> ++ <source>List View</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/list-view-factory.h" line="48"/> ++ <location filename="../../libpeony-qt/controls/directory-view/directory-view-factory/list-view-factory.h" line="101"/> ++ <source>Show the folder children as rows in a list.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Basic Preview Page</source> ++ <translation type="vanished">基本</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page-factory.h" line="40"/> ++ <source>Basic</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page-factory.h" line="46"/> ++ <source>Show the basic file properties, and allow you to modify the access and name.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Permissions Page</source> ++ <translation type="vanished">权限</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page-factory.h" line="41"/> ++ <source>Permissions</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/permissions-properties-page-factory.h" line="47"/> ++ <source>Show and modify file's permission, owner and group.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Can not trash</source> ++ <translation type="vanished">不能回收</translation> ++ </message> ++ <message> ++ <source>Can not trash these files. You can delete them permanently. Are you sure doing that?</source> ++ <translation type="vanished">这些文件不能完全放入回收站,可以选择永久删除这些文件,确定这样做吗?</translation> ++ </message> ++ <message> ++ <source>Can not trash files more than 10GB, would you like to delete it permanently?</source> ++ <translation type="vanished">无法回收大于10G的文件,是否需要永久删除?</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="148"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="108"/> ++ <source>The file is too large to be moved to the recycle bin. Do you want to permanently delete it?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="152"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="111"/> ++ <source>These files are too large to be moved to the recycle bin. Do you want to permanently delete these %1 files?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="321"/> ++ <source>Clean the Trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>OK</source> ++ <translation type="vanished">清空回收站</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="333"/> ++ <source>Do you want to empty the recycle bin and delete the files permanently? Once it has begun there is no way to restore them.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Delete Permanently</source> ++ <translation type="vanished">永久删除</translation> ++ </message> ++ <message> ++ <source>Are you sure that you want to delete these files? Once you start a deletion, the files deleting will never be restored again.</source> ++ <translation type="vanished">您确定要删除这些文件吗?一旦开始删除,这些文件将不可再恢复。</translation> ++ </message> ++ <message> ++ <source>Computer Properties Page</source> ++ <translation type="vanished">计算机</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page-factory.h" line="41"/> ++ <source>Computer Properties</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page-factory.h" line="47"/> ++ <source>Show the computer properties or items in computer.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Trash and Recent Properties Page</source> ++ <translation type="vanished">最近/回收</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page-factory.h" line="40"/> ++ <source>Trash and Recent</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/recent-and-trash-properties-page-factory.h" line="46"/> ++ <source>Show the file properties or items in trash or recent.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>eject device failed</source> ++ <translation type="vanished">弹出设备失败</translation> ++ </message> ++ <message> ++ <source>Please check whether the device is occupied and then eject the device again</source> ++ <translation type="vanished">请检查设备是否正在使用,确认没有使用后再次弹出</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="544"/> ++ <source>Format failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="546"/> ++ <source>YES</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1033"/> ++ <source>Formatting successful! But failed to set the device name.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1050"/> ++ <source>qmesg_notify</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1065"/> ++ <source>Format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1067"/> ++ <source>Begin Format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1070"/> ++ <source>Close</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1031"/> ++ <source>Format operation has been finished successfully.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Formatting successful! Description Failed to set the device name.</source> ++ <translation type="vanished">格式化成功!设备名设置失败。</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1050"/> ++ <source>Sorry, the format operation is failed!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1063"/> ++ <source>Formatting this volume will erase all data on it. Please backup all retained data before formatting. Do you want to continue ?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1031"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1033"/> ++ <source>format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>begin format</source> ++ <translation type="vanished">开始</translation> ++ </message> ++ <message> ++ <source>close</source> ++ <translation type="vanished">关闭</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/sync-thread.cpp" line="63"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="950"/> ++ <source>File Manager</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/search-vfs-register.h" line="40"/> ++ <source>Default search vfs of peony</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="172"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1752"/> ++ <source>Force unmount failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-net-work-item.cpp" line="136"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="172"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1752"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="542"/> ++ <source>Error: %1 ++</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-net-work-item.cpp" line="142"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1755"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1805"/> ++ <source>Data synchronization is complete,the device has been unmount successfully!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-net-work-item.cpp" line="131"/> ++ <location filename="../../libpeony-qt/model/side-bar-net-work-item.cpp" line="136"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1784"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1787"/> ++ <source>Unmount failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1772"/> ++ <source>Not authorized to perform operation.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-net-work-item.cpp" line="131"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1784"/> ++ <source>Unable to unmount it, you may need to close some programs, such as: GParted etc.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1787"/> ++ <source>Error: %1 ++Do you want to unmount forcely?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="326"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Eject Anyway</source> ++ <translation type="vanished">无论如何弹出</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1245"/> ++ <source>Failed to activate device: Incorrect passphrase</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1255"/> ++ <source>The device has been mount successfully!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1450"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1488"/> ++ <source>Eject device failed, the reason may be that the device has been removed, etc.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1456"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1495"/> ++ <source>Data synchronization is complete and the device can be safely unplugged!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1991"/> ++ <source>Password is empty, please re-enter!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Unable to eject %1</source> ++ <translation type="vanished">无法弹出 %1</translation> ++ </message> ++ <message> ++ <source>PeonyNotify</source> ++ <translation type="vanished">文件管理器通知</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1452"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1490"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1772"/> ++ <source>Eject failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="266"/> ++ <source>favorite</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="269"/> ++ <source>Favorites</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="304"/> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="309"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="382"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="387"/> ++ <source>File is not existed.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="317"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="395"/> ++ <source>Share Data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="321"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="399"/> ++ <source>Trash</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="325"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="403"/> ++ <source>Recent</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-file.cpp" line="337"/> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-file.cpp" line="428"/> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-file.cpp" line="462"/> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="371"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="250"/> ++ <source>Operation not supported</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="466"/> ++ <source>The virtual file system does not support folder creation</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="538"/> ++ <source>Can not create a symbolic file for vfs location</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="545"/> ++ <source>Symbolic Link</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="557"/> ++ <source>Can not create symbolic file here, %1</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="566"/> ++ <source>Can not add a file to favorite directory.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="624"/> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="632"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="231"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="267"/> ++ <source>The virtual file system cannot be opened</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-file.cpp" line="378"/> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-file.cpp" line="520"/> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-file.cpp" line="563"/> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-file.cpp" line="577"/> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="453"/> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="481"/> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="496"/> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="582"/> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="600"/> ++ <source>Virtual file directories do not support move and copy operations</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/favorite-vfs-register.h" line="43"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-register.h" line="42"/> ++ <source>Default favorite vfs of peony</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page-factory.h" line="38"/> ++ <location filename="../../libpeony-qt/controls/property-page/details-properties-page-factory.h" line="44"/> ++ <source>Details</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/mark-properties-page-factory.h" line="40"/> ++ <source>Mark</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/mark-properties-page-factory.h" line="46"/> ++ <source>mark this file.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page-factory.h" line="40"/> ++ <source>Open With</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page-factory.h" line="46"/> ++ <source>open with.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/sync-thread.cpp" line="33"/> ++ <source>It need to synchronize before operating the device,place wait!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-enumerator.cpp" line="412"/> ++ <location filename="../../libpeony-qt/file-enumerator.cpp" line="425"/> ++ <source>Unable to discover the file, it may have been removed or deleted.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-enumerator.cpp" line="419"/> ++ <source>permission denied</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>file not found</source> ++ <translation type="vanished">没有发现该文件</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="539"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="556"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="572"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="866"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="162"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="184"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="206"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="213"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="229"/> ++ <source>duplicate</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <source>Error when getting information for file : No target file found</source> ++ <translation type="vanished">获取文件信息时出现错误:没有目标文件。</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="353"/> ++ <source>data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="371"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="345"/> ++ <source>label</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-file-system-item.cpp" line="185"/> ++ <location filename="../../libpeony-qt/vfs/search-vfs-uri-parser.cpp" line="110"/> ++ <source>Computer</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="356"/> ++ <location filename="../../libpeony-qt/model/side-bar-file-system-item.cpp" line="257"/> ++ <source>File System</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/side-bar-file-system-item.cpp" line="262"/> ++ <source>Data</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="411"/> ++ <source>Failed to open file "%1": insufficient permissions.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="422"/> ++ <source>File “%1” does not exist. Please check whether the file has been deleted.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/disc/disccommand.cpp" line="81"/> ++ <source>burn operation has been cancelled</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/disc/disccommand.cpp" line="85"/> ++ <source> is busy!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1298"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="156"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="388"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="182"/> ++ <source>Are you sure you want to permanently delete this file? Once deletion begins, the file will not be recoverable.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1302"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="160"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="392"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="186"/> ++ <source>Are you sure you want to permanently delete these %1 files? Once deletion begins, these file will not be recoverable.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="283"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="416"/> ++ <source>Virtual file directories do not support move operations</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-register.h" line="41"/> ++ <source>test simplify vfs plugin</source> ++ <translation></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-register.h" line="73"/> ++ <source>Default custom vfs info of peony</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/custom-vfs-register.h" line="115"/> ++ <source>Default local vfs info of peony</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window-factory.h" line="61"/> ++ <source>Show properties plugin window.</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>UdfBurn::UdfAppendBurnDataDialog</name> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="45"/> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="180"/> ++ <source>AppendBurnData</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="58"/> ++ <source>Disc Type:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="66"/> ++ <source>Device Name:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="79"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="81"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="89"/> ++ <source>Unknown</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="131"/> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="148"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="131"/> ++ <source>No burn data, please add!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="148"/> ++ <source>The disc name cannot be set to empty, please re-enter it!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="180"/> ++ <source>AppendBurnData operation has been finished successfully.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="186"/> ++ <source>Sorry, the appendBurnData operation is failed!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="187"/> ++ <source>Failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="196"/> ++ <source>Burning. Do not close this window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="205"/> ++ <source>Burning this disc will append datas on it. Do you want to continue ?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="206"/> ++ <source>Burn</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="208"/> ++ <source>Begin Burning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfAppendBurnDataDialog.cpp" line="209"/> ++ <source>Close</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>UdfBurn::UdfFormatDialog</name> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="47"/> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="166"/> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="217"/> ++ <source>Format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="60"/> ++ <source>Disc Type:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="68"/> ++ <source>Device Name:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="82"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="84"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="92"/> ++ <source>Unknown</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="131"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="131"/> ++ <source>The disc name cannot be set to empty, please re-enter it!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="166"/> ++ <source>Format operation has been finished successfully.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="171"/> ++ <source>Sorry, the format operation is failed!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="172"/> ++ <source>Failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="206"/> ++ <source>Formatting. Do not close this window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="216"/> ++ <source>Formatting this disc will erase all data on it. Please backup all retained data before formatting. Do you want to continue ?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="219"/> ++ <source>Begin Format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="220"/> ++ <source>Close</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++<context> ++ <name>UdfFormatDialog</name> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="41"/> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="165"/> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="218"/> ++ <source>Format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="54"/> ++ <source>Disc Type:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="62"/> ++ <source>Device Name:</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="76"/> ++ <source>OK</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="78"/> ++ <source>Cancel</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="86"/> ++ <source>Unknown</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="128"/> ++ <source>Warning</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="128"/> ++ <source>The disc name cannot be set to empty, please re-enter it!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="165"/> ++ <source>Format operation has been finished successfully.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="170"/> ++ <source>Sorry, the format operation is failed!</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="171"/> ++ <source>Failed</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="205"/> ++ <source>Formatting. Do not close this window</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="217"/> ++ <source>Formatting this disc will erase all data on it. Please backup all retained data before formatting. Do you want to continue ?</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="220"/> ++ <source>Begin Format</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/udfFormatDialog.cpp" line="221"/> ++ <source>Close</source> ++ <translation type="unfinished"></translation> ++ </message> ++</context> ++</TS> +diff --git a/translations/libpeony-qt/libpeony-qt_zh_CN.ts b/translations/libpeony-qt/libpeony-qt_zh_CN.ts +index c36e1e7..ea35331 100644 +--- a/translations/libpeony-qt/libpeony-qt_zh_CN.ts ++++ b/translations/libpeony-qt/libpeony-qt_zh_CN.ts +@@ -159,13 +159,13 @@ + </message> + <message> + <location filename="../../libpeony-qt/model/file-label-model.cpp" line="130"/> +- <location filename="../../libpeony-qt/model/file-label-model.cpp" line="435"/> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="438"/> + <source>Error</source> + <translation>错误</translation> + </message> + <message> + <location filename="../../libpeony-qt/model/file-label-model.cpp" line="130"/> +- <location filename="../../libpeony-qt/model/file-label-model.cpp" line="435"/> ++ <location filename="../../libpeony-qt/model/file-label-model.cpp" line="438"/> + <source>Label or color is duplicated.</source> + <translation>标签或者颜色重复</translation> + </message> +@@ -173,8 +173,8 @@ + <context> + <name>FileOperationHelper</name> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-helper.cpp" line="157"/> +- <location filename="../../libpeony-qt/file-operation/file-operation-helper.cpp" line="175"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-helper.cpp" line="162"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-helper.cpp" line="177"/> + <source>Burn failed</source> + <translation>刻录失败</translation> + </message> +@@ -318,68 +318,68 @@ + <translation>格式化为ext4文件系统时可能会导致其他用户无法读写U盘</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="246"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="253"/> + <source>Cancel</source> + <translation>关闭</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="247"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="254"/> + <source>OK</source> + <translation>确定</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="361"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="375"/> + <source>Data</source> + <translation>数据盘</translation> + </message> + <message> + <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="238"/> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="567"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="615"/> + <source>Warning</source> + <translation>警告</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="567"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="615"/> + <source>Device name cannot start with a decimal point, Please re-enter!</source> + <translation>设备名称不能以小数点开始,请重新输入!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="578"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="626"/> + <source>Enter Password:</source> + <translation>输入密码:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="596"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="644"/> + <source>Password too short, please retype a password more than 6 characters</source> + <translation>密码过短, 请重新输入大于6位的密码</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="695"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="743"/> + <source>%1/sec, %2 remaining.</source> + <translation>%1/秒, 剩余%2.</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="695"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="743"/> + <source>over one day</source> + <translation>超过一天</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="697"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="745"/> + <source>getting progress...</source> + <translation>正在获取进度...</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1290"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1343"/> + <source>Error</source> + <translation>错误</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1290"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1343"/> + <source>Block not existed!</source> + <translation>设备不存在!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1337"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1390"/> + <source>Formatting. Do not close this window</source> + <translation>正在格式化, 请勿关闭</translation> + </message> +@@ -387,145 +387,153 @@ + <context> + <name>KyFileDialogRename</name> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="97"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="112"/> + <source>Renaming "%1"</source> + <translation>正在重命名 "%1"</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="98"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="113"/> + <source>Renaming failed, the reason is: %1</source> + <translation>重命名失败, 原因: %1</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="98"/> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="107"/> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="116"/> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="121"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="113"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="122"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="131"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="136"/> + <source>Filename too long</source> + <translation>文件名过长</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="102"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="117"/> + <source>Copying "%1"</source> + <translation>正在复制 "%1"</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="106"/> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="115"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="121"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="130"/> + <source>To "%1"</source> + <translation>到 "%1"</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="107"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="122"/> + <source>Copying failed, the reason is: %1</source> + <translation>复制失败, 原因: %1</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="111"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="126"/> + <source>Moving "%1"</source> + <translation>正在移动 "%1"</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="116"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="131"/> + <source>Moving failed, the reason is: %1</source> + <translation>移动失败, 原因: %1</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="120"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="135"/> + <source>File operation error:</source> + <translation>文件操作错误:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="121"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="136"/> + <source>The reason is: %1</source> + <translation>原因: %1</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="140"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="155"/> + <source>Truncation</source> + <translation>截断</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="141"/> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="213"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="156"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="228"/> + <source>Save</source> + <translation>保存</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="171"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="186"/> + <source>All applications</source> + <translation>全部应用</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="174"/> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="212"/> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="272"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="189"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="227"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="287"/> + <source>Cancel</source> + <translation>取消</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="175"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="190"/> + <source>Apply</source> + <translation>应用</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="208"/> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="258"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="223"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="273"/> + <source>Bytes</source> + <translation>字节</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="264"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="279"/> + <source>Front truncation</source> + <translation>前截断</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="265"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="280"/> + <source>Post truncation</source> + <translation>后截断</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="321"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="336"/> + <source>Description: Skip copying files of the current type</source> + <translation>说明:跳过当前类型文件的复制</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="326"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="341"/> + <source>truncate interval</source> + <translation>截断区间</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="327"/> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="345"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="342"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="360"/> + <source>.</source> + <translation></translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="328"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="343"/> ++ <source>Explanation: Truncate the portion of the file name that exceeds %1 bytes and select</source> ++ <translation>说明:截断文件名超过 %1 字节的部分,去选择</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="361"/> ++ <source>Explanation: When renaming a file name, ensure it is within %1 bytes and </source> ++ <translation>说明:用户重命名文件名,保证在 %1 字节内,去 </translation> ++ </message> ++ <message> + <source>Explanation: Truncate the portion of the file name that exceeds 225 bytes and select</source> +- <translation>说明:截断文件名的超过225字节的部分,去选择</translation> ++ <translation type="vanished">说明:截断文件名的超过225字节的部分,去选择</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="337"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="352"/> + <source>Description: By default, save to "%1/扩展".</source> + <translation>说明:默认保存至"%1/扩展"。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="344"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="359"/> + <source>modify the name</source> + <translation>修改命名</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="346"/> + <source>Explanation: When renaming a file name, ensure it is within 255 bytes and </source> +- <translation>说明:用户重命名文件名,保证在225字节以内,去 {255 ?} </translation> ++ <translation type="vanished">说明:用户重命名文件名,保证在225字节以内,去 {255 ?} </translation> + </message> + <message> + <source>Explanation: When renaming a file name, ensure it is within 225 bytes and </source> + <translation type="vanished">说明:用户重命名文件名,保证在225字节以内,去</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="139"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="154"/> + <source>Skip</source> + <translation>跳过</translation> + </message> +@@ -534,7 +542,7 @@ + <translation type="vanished">全部跳过</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="142"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="157"/> + <source>Rename</source> + <translation>重命名</translation> + </message> +@@ -543,7 +551,7 @@ + <translation type="vanished">请输入文件名</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="273"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-dialog/kyfiledialogrename.cpp" line="288"/> + <source>OK</source> + <translation>确定</translation> + </message> +@@ -551,84 +559,97 @@ + <context> + <name>MainProgressBar</name> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="438"/> + <source>File operation</source> +- <translation>文件操作</translation> ++ <translation type="vanished">文件操作</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="493"/> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.h" line="319"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="530"/> + <source>starting ...</source> + <translation>正在开始 ...</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="457"/> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="598"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="494"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="637"/> + <source>cancel all file operations</source> + <translation>取消所有文件操作</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="447"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="484"/> + <source>Minimize</source> + <translation>最小化</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="455"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="492"/> + <source>Close</source> + <translation>关闭</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="458"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="495"/> + <source>Are you sure to cancel all file operations?</source> + <translation>确定取消所有文件操作?</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="599"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="638"/> + <source>Are you sure want to cancel all file operations</source> + <translation>确定要取消所有文件操作?</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="460"/> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="601"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="497"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="640"/> + <source>OK</source> + <translation>确定</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="461"/> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="602"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="475"/> ++ <source>File Operation</source> ++ <translation>文件操作</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="498"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="641"/> + <source>Cancel</source> + <translation>取消</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="634"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="531"/> ++ <source>Calculating time</source> ++ <translation>正在计算时间</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="673"/> + <source>continue</source> + <translation>继续</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="636"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="675"/> + <source>pause</source> + <translation>暂停</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="716"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="755"/> + <source>canceling ...</source> + <translation>取消中 ...</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="719"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="758"/> + <source>sync ...</source> + <translation>正在同步...</translation> + </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="789"/> ++ <source> %1Mb/s Est. time left: %2</source> ++ <translation>%1Mb/s 预计剩余时间: %2</translation> ++ </message> + </context> + <context> + <name>MessageDialog</name> + <message> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1837"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1867"/> + <source>Peony</source> + <translation>文件管理器</translation> + </message> + <message> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1864"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1894"/> + <source>Forcibly pulling out the device may cause data + loss or device exceptions!</source> + <translation>强制拔出设备可能会导致数据丢失 +@@ -638,8 +659,12 @@ + <context> + <name>OtherButton</name> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="825"/> + <source>Other queue</source> ++ <translation type="vanished">其它队列</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="871"/> ++ <source>Other Queue</source> + <translation>其它队列</translation> + </message> + </context> +@@ -761,123 +786,88 @@ + <translation type="vanished">搜索文件名或者内容请至少指定一个!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="57"/> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="59"/> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="60"/> + <source>all</source> +- <translation>全部</translation> ++ <translation type="vanished">全部</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="57"/> + <source>file folder</source> +- <translation>文件夹</translation> ++ <translation type="vanished">文件夹</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="57"/> + <source>image</source> +- <translation>图片</translation> ++ <translation type="vanished">图片</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="57"/> + <source>video</source> +- <translation>视频</translation> ++ <translation type="vanished">视频</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="58"/> + <source>text file</source> +- <translation>文本</translation> ++ <translation type="vanished">文本</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="58"/> + <source>audio</source> +- <translation>音频</translation> ++ <translation type="vanished">音频</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="58"/> + <source>others</source> +- <translation>其它</translation> ++ <translation type="vanished">其它</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="58"/> + <source>wps file</source> +- <translation>WPS文件</translation> ++ <translation type="vanished">WPS文件</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="59"/> + <source>today</source> +- <translation>今天</translation> ++ <translation type="vanished">今天</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="59"/> + <source>this week</source> +- <translation>本周</translation> ++ <translation type="vanished">本周</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="59"/> + <source>this month</source> +- <translation>本月</translation> ++ <translation type="vanished">本月</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="59"/> + <source>this year</source> +- <translation>今年</translation> +- </message> +- <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="59"/> +- <source>yesterday</source> +- <translation>昨天</translation> +- </message> +- <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="59"/> +- <source>last week</source> +- <translation>上周</translation> +- </message> +- <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="59"/> +- <source>last month</source> +- <translation>上月</translation> +- </message> +- <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="59"/> +- <source>last year</source> +- <translation>去年</translation> ++ <translation type="vanished">今年</translation> + </message> + <message> + <source>year ago</source> + <translation type="vanished">去年</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="60"/> + <source>tiny(0-16K)</source> +- <translation>极小(0-16K)</translation> ++ <translation type="vanished">极小(0-16K)</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="60"/> + <source>small(16k-1M)</source> +- <translation>较小(16k-1M)</translation> ++ <translation type="vanished">较小(16k-1M)</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="60"/> + <source>medium(1M-100M)</source> +- <translation>中等(1M-100M)</translation> ++ <translation type="vanished">中等(1M-100M)</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="60"/> + <source>big(100M-1G)</source> +- <translation>较大(100M-1G)</translation> ++ <translation type="vanished">较大(100M-1G)</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/advance-search-bar.h" line="60"/> + <source>large(>1G)</source> +- <translation>极大(>1G)</translation> ++ <translation type="vanished">极大(>1G)</translation> + </message> + </context> + <context> + <name>Peony::AdvancedLocationBar</name> + <message> +- <location filename="../../libpeony-qt/controls/navigation-bar/advanced-location-bar.cpp" line="200"/> + <source>Search Content...</source> +- <translation>搜索内容</translation> ++ <translation type="vanished">搜索内容</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/navigation-bar/advanced-location-bar.cpp" line="244"/> ++ <source>Search File</source> ++ <translation>搜索文件</translation> + </message> + </context> + <context> +@@ -947,27 +937,27 @@ + <context> + <name>Peony::AllFileLaunchDialog</name> + <message> +- <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="380"/> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="393"/> + <source>Choose new application</source> + <translation>选择一个新的应用</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="382"/> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="395"/> + <source>Choose an Application to open this file</source> + <translation>选择一个应用打开这个文件</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="389"/> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="402"/> + <source>apply now</source> + <translation>立即应用</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="395"/> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="409"/> + <source>OK</source> + <translation>确定</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="396"/> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="413"/> + <source>Cancel</source> + <translation>取消</translation> + </message> +@@ -982,7 +972,7 @@ + <context> + <name>Peony::BasicPropertiesPage</name> + <message> +- <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="935"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1020"/> + <source>Choose a custom icon</source> + <translation>选择自定义图标</translation> + </message> +@@ -1022,12 +1012,12 @@ + <translation type="vanished">移动</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="451"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="455"/> + <source>symbolLink</source> + <translation>快捷方式</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="455"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="459"/> + <source>Folder</source> + <translation>文件夹</translation> + </message> +@@ -1100,33 +1090,38 @@ + <translation>访问时间:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="322"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="323"/> + <source>Readonly</source> + <translation>只读</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="323"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="324"/> + <source>Hidden</source> + <translation>隐藏</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="332"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="327"/> ++ <source>Readonly (just applied by subfiles)</source> ++ <translation>只读(仅对文件夹下文件)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="337"/> + <source>Property:</source> + <translation>属性:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="614"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="642"/> + <source>usershare</source> + <translation>本机共享</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="748"/> +- <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="989"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="776"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1074"/> + <source>%1 (%2 Bytes)</source> + <translation>%1 (%2 字节)</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="943"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1028"/> + <source>Please select a image that is smaller than 1MB.</source> + <translation>请选择小于1MB的图片。</translation> + </message> +@@ -1143,7 +1138,7 @@ + <translation type="vanished">不能移动一个文件夹到它内部!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="982"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1067"/> + <source>%1 Bytes</source> + <translation>%1 字节</translation> + </message> +@@ -1160,15 +1155,15 @@ + <translation type="vanished">%1 GB (%2 字节)</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1002"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1087"/> + <source>%1 files, %2 folders</source> + <translation>%1 个文件, %2 个文件夹</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1118"/> +- <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1120"/> +- <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1125"/> +- <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1127"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1203"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1205"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1210"/> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1212"/> + <source>Can't get remote file information</source> + <translation>未能获取远程文件信息</translation> + </message> +@@ -1184,87 +1179,124 @@ + <context> + <name>Peony::ComputerPropertiesPage</name> + <message> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="102"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="107"/> + <source>CPU Name:</source> + <translation>处理器:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="103"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="108"/> + <source>CPU Core:</source> + <translation>核心数:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="104"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="109"/> + <source>Memory Size:</source> + <translation>内存:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="116"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="121"/> + <source>User Name: </source> + <translation>用户名:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="117"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="122"/> + <source>Desktop: </source> + <translation>桌面环境:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="125"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="130"/> + <source>You should mount this volume first</source> + <translation>你需要挂载该卷才能查看信息</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="142"/> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="240"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="147"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="385"/> + <source>Name: </source> + <translation>分区名:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="142"/> + <source>File System</source> +- <translation>文件系统</translation> ++ <translation type="vanished">文件系统</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="142"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="147"/> + <source>Data</source> + <translation>数据盘</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="143"/> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="245"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="147"/> ++ <source>System Disk</source> ++ <translation>系统盘</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="148"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="390"/> + <source>Total Space: </source> + <translation>总容量:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="144"/> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="246"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="149"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="391"/> + <source>Used Space: </source> + <translation>使用空间:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="145"/> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="247"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="154"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="155"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="156"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="161"/> ++ <source>In calculation...</source> ++ <translation>计算中...</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="157"/> ++ <source>/root used: </source> ++ <translation>/root 占用:</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="158"/> ++ <source>/home used: </source> ++ <translation>/home 占用:</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="159"/> ++ <source>/usershare used: </source> ++ <translation>/usershare 占用:</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="165"/> ++ <source>/data/* used: </source> ++ <translation>/data/* 占用:</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="184"/> ++ <source>Unknow (No permission)</source> ++ <translation>未知(无权限查看)</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="281"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="392"/> + <source>Free Space: </source> + <translation>剩余空间:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="146"/> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="249"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="282"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="394"/> + <source>Type: </source> + <translation>文件系统:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="262"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="407"/> + <source>Kylin Burner</source> + <translation>刻录</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="268"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="417"/> + <source>Open with: </source> + <translation>打开:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="275"/> ++ <location filename="../../libpeony-qt/controls/property-page/computer-properties-page.cpp" line="424"/> + <source>Unknown</source> + <translation>未知的分区</translation> + </message> +@@ -1288,54 +1320,54 @@ + <translation type="vanished">类型</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="143"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="145"/> + <source>Connect to server</source> + <translation>连接服务器</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="168"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="170"/> + <source>Ip</source> + <translation>服务器</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="170"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="172"/> + <source>Port</source> + <translation>端口</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="171"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="173"/> + <source>Type</source> + <translation>类型</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="202"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="204"/> + <source>Personal Collection server:</source> + <translation>个人收藏服务器</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="215"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="217"/> + <source>Add</source> + <translation>添加</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="216"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="218"/> + <source>Delete</source> + <translation>删除</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="217"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="219"/> + <source>Connect</source> + <translation>连接</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="296"/> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="387"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="299"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="390"/> + <source>Ip input error, please re-enter!</source> + <translation>IP输入错误, 请重新输入!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="300"/> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="391"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="303"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="394"/> + <source>Port input error, please re-enter!</source> + <translation>端口号输入错误, 请重新输入!</translation> + </message> +@@ -1353,10 +1385,10 @@ + <translation type="vanished">连接</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="296"/> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="300"/> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="387"/> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="391"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="299"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="303"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="390"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="394"/> + <source>Warning</source> + <translation>警告</translation> + </message> +@@ -1372,42 +1404,47 @@ + <context> + <name>Peony::ConnectServerLogin</name> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="487"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="493"/> + <source>The login user</source> + <translation>登录身份</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="496"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="502"/> + <source>Please enter the %1's user name and password of the server.</source> + <translation>请输入服务器 %1 的用户名和密码。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="503"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="509"/> + <source>User's identity</source> + <translation>连接身份</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="504"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="510"/> + <source>Guest</source> + <translation>游客(匿名登录)</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="520"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="528"/> + <source>Name</source> + <translation>名称</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="521"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="529"/> + <source>Password</source> + <translation>密码</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="542"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="531"/> ++ <source>domain</source> ++ <translation>域</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="556"/> + <source>Cancel</source> + <translation>取消</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="543"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="557"/> + <source>OK</source> + <translation>确定</translation> + </message> +@@ -1416,7 +1453,7 @@ + <translation type="vanished">游客(匿名登录)</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="505"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="511"/> + <source>Registered users</source> + <translation>注册用户</translation> + </message> +@@ -1429,7 +1466,7 @@ + <translation type="vanished">密码</translation> + </message> + <message> +- <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="522"/> ++ <location filename="../../libpeony-qt/connect-to-server-dialog.cpp" line="530"/> + <source>Remember the password</source> + <translation>记住密码</translation> + </message> +@@ -1445,69 +1482,67 @@ + <context> + <name>Peony::CreateLinkInternalPlugin</name> + <message> +- <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="132"/> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="157"/> + <source>Create Link to Desktop</source> + <translation>发送到桌面快捷方式</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="158"/> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="183"/> + <source>Create Link to...</source> + <translation>发送快捷方式到...</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="161"/> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="186"/> + <source>Choose a Directory to Create Link</source> + <translation>选择创建链接的目录</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.h" line="68"/> + <source>Peony-Qt Create Link Extension</source> +- <translation>创建链接</translation> ++ <translation type="vanished">创建链接</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.h" line="71"/> + <source>Create Link Menu Extension.</source> +- <translation>创建链接.</translation> ++ <translation type="vanished">创建链接.</translation> + </message> + </context> + <context> + <name>Peony::CreateSharedFileLinkMenuPlugin</name> + <message> +- <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="271"/> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="296"/> + <source>Create Link to Desktop</source> + <translation>发送到桌面快捷方式</translation> + </message> ++</context> ++<context> ++ <name>Peony::CreateTemplateOperation</name> + <message> +- <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.h" line="161"/> +- <source>Peony-Qt Share File menu Extension</source> +- <translation type="unfinished"></translation> ++ <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="85"/> ++ <source>File create error</source> ++ <translation>创建文件错误</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.h" line="164"/> +- <source>Tag with Menu.</source> +- <translation type="unfinished"></translation> ++ <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="87"/> ++ <source>Can not create %1: Read-only mode, can not write-in</source> ++ <translation>无法创建 %1:只读模式,无法写入</translation> + </message> +-</context> +-<context> +- <name>Peony::CreateTemplateOperation</name> + <message> +- <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="76"/> ++ <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="97"/> + <source>NewFile</source> + <translation>新建文件</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="92"/> ++ <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="117"/> + <source>Create file</source> + <translation>文件创建</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="105"/> ++ <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="130"/> + <source>NewFolder</source> + <translation>新建文件夹</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="124"/> +- <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="158"/> ++ <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="153"/> ++ <location filename="../../libpeony-qt/file-operation/create-template-operation.cpp" line="191"/> + <source>Create file error</source> + <translation>创建文件错误</translation> + </message> +@@ -1528,7 +1563,7 @@ + <context> + <name>Peony::DefaultAcitonWidget</name> + <message> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="531"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="554"/> + <source>No default app</source> + <translation>没有设置默认打开方式</translation> + </message> +@@ -1536,18 +1571,22 @@ + <context> + <name>Peony::DefaultOpenWithWidget</name> + <message> +- <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="439"/> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="457"/> + <source>No default app</source> + <translation>没有设置默认打开方式</translation> + </message> + </context> + <context> + <name>Peony::DefaultPreviewPage</name> ++ <message> ++ <source>Select the file you want to preview...</source> ++ <translation type="vanished">选择你想要预览的文件...</translation> ++ </message> + <message> + <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="75"/> + <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="216"/> +- <source>Select the file you want to preview...</source> +- <translation>选择你想要预览的文件...</translation> ++ <source>Select the file you want to preview</source> ++ <translation>选择你想要预览的文件</translation> + </message> + <message> + <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="207"/> +@@ -1669,17 +1708,16 @@ + <context> + <name>Peony::DirectoryView::IconView</name> + <message> +- <location filename="../../libpeony-qt/controls/directory-view/view/icon-view/icon-view.h" line="68"/> + <source>Icon View</source> +- <translation>图标视图</translation> ++ <translation type="vanished">图标视图</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/directory-view/view/icon-view/icon-view.cpp" line="334"/> ++ <location filename="../../libpeony-qt/controls/directory-view/view/icon-view/icon-view.cpp" line="337"/> + <source>warn</source> + <translation>警告</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/directory-view/view/icon-view/icon-view.cpp" line="334"/> ++ <location filename="../../libpeony-qt/controls/directory-view/view/icon-view/icon-view.cpp" line="337"/> + <source>This operation is not supported.</source> + <translation>不支持此操作。</translation> + </message> +@@ -1694,17 +1732,16 @@ + <context> + <name>Peony::DirectoryView::ListView</name> + <message> +- <location filename="../../libpeony-qt/controls/directory-view/view/list-view/list-view.h" line="63"/> + <source>List View</source> +- <translation>列表视图</translation> ++ <translation type="vanished">列表视图</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/directory-view/view/list-view/list-view.cpp" line="605"/> ++ <location filename="../../libpeony-qt/controls/directory-view/view/list-view/list-view.cpp" line="622"/> + <source>warn</source> + <translation>警告</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/directory-view/view/list-view/list-view.cpp" line="605"/> ++ <location filename="../../libpeony-qt/controls/directory-view/view/list-view/list-view.cpp" line="622"/> + <source>This operation is not supported.</source> + <translation>不支持此操作。</translation> + </message> +@@ -1783,7 +1820,7 @@ + <translation type="vanished">文件夹(&F)</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="691"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="696"/> + <source>New Folder</source> + <translation>新建文件夹</translation> + </message> +@@ -1804,17 +1841,17 @@ + <translation type="vanished">排序类型...</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="743"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="748"/> + <source>Name</source> + <translation>文件名称</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="745"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="750"/> + <source>File Type</source> + <translation>文件类型</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="746"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="751"/> + <source>File Size</source> + <translation>文件大小</translation> + </message> +@@ -1824,72 +1861,82 @@ + </message> + <message> + <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="322"/> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="413"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="426"/> + <source>Open in New Window</source> + <translation>在新窗口中打开</translation> + </message> + <message> + <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="332"/> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="423"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="436"/> + <source>Open in New Tab</source> + <translation>在新标签页中打开</translation> + </message> + <message> + <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="380"/> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="435"/> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="499"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="448"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="512"/> + <source>Open</source> + <translation>打开</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="391"/> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="451"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="387"/> ++ <source>Open failed</source> ++ <translation>无法打开</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="387"/> ++ <source>Open directory failed, you have no permission!</source> ++ <translation>无法打开文件夹,没有权限!</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="404"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="464"/> + <source>Open with...</source> + <translation>打开方式</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="406"/> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="488"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="419"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="501"/> + <source>More applications...</source> + <translation>更多应用</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="508"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="521"/> + <source>Open %1 selected files</source> + <translation>打开%1个选中文件</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="556"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="561"/> + <source>New</source> + <translation>新建</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="675"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="680"/> + <source>Empty File</source> + <translation>空文本</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="687"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="692"/> + <source>Folder</source> + <translation>文件夹</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="717"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="722"/> + <source>View Type</source> + <translation>视图类型</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="737"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="742"/> + <source>Sort By</source> + <translation>排序类型</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="744"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="749"/> + <source>Modified Date</source> + <translation>修改日期</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="747"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="752"/> + <source>Original Path</source> + <translation>原始路径</translation> + </message> +@@ -1898,23 +1945,17 @@ + <translation type="vanished">排序顺序...</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="780"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="790"/> + <source>Ascending Order</source> + <translation>升序</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1470"/> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1480"/> +- <source>Peony-Qt Filesafe Menu Extension</source> +- <translation type="unfinished"></translation> +- </message> +- <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1512"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1613"/> + <source>MultiSelect</source> + <translation>多选</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="779"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="789"/> + <source>Descending Order</source> + <translation>降序</translation> + </message> +@@ -1923,30 +1964,35 @@ + <translation type="vanished">排序偏好...</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="798"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="819"/> + <source>Folder First</source> + <translation>文件夹优先</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="807"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="828"/> + <source>Chinese First</source> + <translation>中文优先</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="816"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="837"/> + <source>Show Hidden</source> + <translation>显示隐藏文件</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="851"/> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="859"/> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1039"/> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1313"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="895"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="903"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1082"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1412"/> + <source>Copy</source> + <translation>复制</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1373"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1403"/> ++ <source>Cancel</source> ++ <translation>取消</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1472"/> + <source>File:"%1" is not exist, did you moved or deleted it?</source> + <translation>文件 ”%s“ 不存在,你是否已经删除或移动到别处?</translation> + </message> +@@ -1955,75 +2001,100 @@ + <translation type="vanished">文件保护箱扩展</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1470"/> + <source>Peony File Labels Menu Extension</source> +- <translation>文件标记</translation> ++ <translation type="vanished">文件标记</translation> + </message> + <message> + <source>&Copy</source> + <translation type="vanished">复制(&C)</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="889"/> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1318"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="932"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1417"/> + <source>Cut</source> + <translation>剪切</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="916"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="959"/> + <source>Delete to trash</source> + <translation>删除到回收站</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="983"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1026"/> + <source>Paste</source> + <translation>粘贴</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1052"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1095"/> + <source>Refresh</source> + <translation>刷新</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1063"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1106"/> + <source>Select All</source> + <translation>全选</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1107"/> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1168"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1153"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1250"/> + <source>Properties</source> + <translation>属性</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1209"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1300"/> + <source>format</source> + <translation>格式化</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1263"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1354"/> + <source>Restore</source> + <translation>还原</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="928"/> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1033"/> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1293"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="971"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1076"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1384"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1402"/> + <source>Delete</source> + <translation>删除</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="772"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="758"/> ++ <source>Path</source> ++ <translation>路径</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="781"/> + <source>Sort Order</source> + <translation>排序顺序</translation> + </message> + <message> + <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="793"/> ++ <source>Newest to oldest</source> ++ <translation>从新到旧</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="794"/> ++ <source>Oldest to newest</source> ++ <translation>从旧到新</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="797"/> ++ <source>Files from large to small</source> ++ <translation>从大到小</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="798"/> ++ <source>Files from small to large</source> ++ <translation>从小到大</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="814"/> + <source>Sort Preferences</source> + <translation>排序偏好</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1372"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1471"/> + <source>Error</source> + <translation>错误</translation> + </message> +@@ -2044,15 +2115,15 @@ + <translation type="vanished">删除到回收站(&D)</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="931"/> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="942"/> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="950"/> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="959"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="974"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="985"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="993"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1002"/> + <source>Delete forever</source> + <translation>永久删除</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="966"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1009"/> + <source>Rename</source> + <translation>重命名</translation> + </message> +@@ -2061,7 +2132,7 @@ + <translation type="vanished">全选(&A)</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1072"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1115"/> + <source>Reverse Select</source> + <translation>反选</translation> + </message> +@@ -2090,7 +2161,7 @@ + <translation type="vanished">属性(&P)</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1236"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1327"/> + <source>&Clean the Trash</source> + <translation>清空回收站(&C)</translation> + </message> +@@ -2111,12 +2182,12 @@ + <translation type="vanished">还原(&R)</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1326"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1425"/> + <source>Clean All</source> + <translation>清空全部</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1343"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1442"/> + <source>Open Parent Folder in New Window</source> + <translation>在新窗口中打开文件所在目录</translation> + </message> +@@ -2131,22 +2202,22 @@ + <context> + <name>Peony::ExtensionsManagerWidget</name> + <message> +- <location filename="../../libpeony-qt/extensions-manager-widget.cpp" line="64"/> ++ <location filename="../../libpeony-qt/extensions-manager-widget.cpp" line="70"/> + <source>Extensions Manager</source> + <translation>插件管理设置</translation> + </message> + <message> +- <location filename="../../libpeony-qt/extensions-manager-widget.cpp" line="68"/> ++ <location filename="../../libpeony-qt/extensions-manager-widget.cpp" line="74"/> + <source>Available extensions</source> + <translation>可使用的插件</translation> + </message> + <message> +- <location filename="../../libpeony-qt/extensions-manager-widget.cpp" line="70"/> ++ <location filename="../../libpeony-qt/extensions-manager-widget.cpp" line="76"/> + <source>Ok</source> + <translation>确定</translation> + </message> + <message> +- <location filename="../../libpeony-qt/extensions-manager-widget.cpp" line="71"/> ++ <location filename="../../libpeony-qt/extensions-manager-widget.cpp" line="77"/> + <source>Cancel</source> + <translation>取消</translation> + </message> +@@ -2280,44 +2351,44 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <context> + <name>Peony::FileCopy</name> + <message> +- <location filename="../../libpeony-qt/file-copy.cpp" line="174"/> +- <location filename="../../libpeony-qt/file-copy.cpp" line="182"/> +- <location filename="../../libpeony-qt/file-copy.cpp" line="202"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="175"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="183"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="203"/> + <source>Error in source or destination file path!</source> + <translation>源地址或者目标文件路径出错!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-copy.cpp" line="191"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="192"/> + <source>Error when copy file: %1, can not copy special files, skip this file and continue?</source> + <translation>拷贝文件: %1 时出错,不能拷贝特殊类型文件,是否跳过此文件并继续?</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-copy.cpp" line="214"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="215"/> + <source>Can not copy %1, file doesn't exist. Has the file been renamed or moved?</source> + <translation>无法拷贝 %1, 文件不存在. 是否被重命名或移动?</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-copy.cpp" line="236"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="237"/> + <source>The dest file "%1" has existed!</source> + <translation>目标文件 %1 已经存在!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-copy.cpp" line="263"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="264"/> + <source>Vfat/FAT32 file systems do not support a single file that occupies more than 4 GB space!</source> + <translation>vfat/fat32文件系统不支持单文件所占空间大于4g</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-copy.cpp" line="288"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="289"/> + <source>Error writing to file: Input/output error</source> + <translation>写如文件出错:输入输出错误</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-copy.cpp" line="456"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="457"/> + <source>Failed to create %1. Please ensure if it is in root directory, or if the device supports gphoto2 protocol correctly.</source> + <translation>创建文件 %1 失败,请确认是否是在根目录创建,或者设备是否正确支持个gphoto2协议。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-copy.cpp" line="462"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="463"/> + <source>Failed to create %1.</source> + <translation>创建文件 %1 失败。</translation> + </message> +@@ -2326,12 +2397,12 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <translation type="vanished">打开源文件或者目标文件出错!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-copy.cpp" line="391"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="392"/> + <source>Please check whether the device has been removed!</source> + <translation>请确认设备是否被移除!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-copy.cpp" line="393"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="394"/> + <source>Write file error: There is no available disk space for device!</source> + <translation>写入文件错误: 设备上没有足够可用空间!</translation> + </message> +@@ -2340,7 +2411,7 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <translation type="vanished">请确认设备空间是否足够!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-copy.cpp" line="450"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="451"/> + <source>File opening failure</source> + <translation>打开文件失败</translation> + </message> +@@ -2349,8 +2420,8 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <translation type="vanished">读和写文件不一致!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-copy.cpp" line="279"/> +- <location filename="../../libpeony-qt/file-copy.cpp" line="408"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="280"/> ++ <location filename="../../libpeony-qt/file-copy.cpp" line="409"/> + <source>operation cancel</source> + <translation>操作取消</translation> + </message> +@@ -2362,59 +2433,81 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <translation type="vanished">文件复制</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="231"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="247"/> + <source>Create folder %1 failed: %2</source> + <translation>创建目录%1失败: %2</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="235"/> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="625"/> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1082"/> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1143"/> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1321"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="251"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="665"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1072"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1087"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1179"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1248"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1427"/> + <source>File copy error</source> + <translation>文件复制错误</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="249"/> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="276"/> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="650"/> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="674"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="270"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="297"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="695"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="719"/> + <source>The file name exceeds the limit</source> + <translation>文件名长度超出限制</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="633"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="258"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="673"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="683"/> + <source>Cannot opening file, permission denied!</source> + <translation>无法打开文件,权限不够!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="635"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="675"/> + <source>File:%1 was not found.</source> + <translation>未找到文件:%1.</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1069"/> ++ <source>open file %1 error: Read-only file system</source> ++ <translation type="vanished">打开文件 %1 出错:只读文件系统</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1166"/> ++ <source>System Disk</source> ++ <translation>系统盘</translation> ++ </message> ++ <message> + <source>File System</source> +- <translation>文件系统</translation> ++ <translation type="vanished">文件系统</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1071"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1075"/> ++ <source>Can not copy %1 to %2: Read-only mode, can not write-in</source> ++ <translation>无法复制 %1 到 %2:只读模式,无法写入</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1090"/> ++ <source>Can not copy %1 to %2: Permission denied</source> ++ <translation>无法复制 %1 到 %2:权限不够</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1168"/> + <source>Data</source> + <translation>数据盘</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1077"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1174"/> + <source>%1 no space left on device. Copy file size: %2 GB, Space needed: %3 GB.</source> + <translation>%1 设备可用空间不足,拷贝文件大小:%2 GB,需要空间:%3 GB。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1189"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1295"/> + <source>Link file error</source> + <translation>创建文件链接失败</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1323"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="1429"/> + <source>Burning does not support replacement</source> + <translation>刻录操作不支持替换文件</translation> + </message> +@@ -2475,24 +2568,24 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <context> + <name>Peony::FileInfo</name> + <message> +- <location filename="../../libpeony-qt/file-info.cpp" line="309"/> ++ <location filename="../../libpeony-qt/file-info.cpp" line="306"/> + <source>data</source> + <translation>数据盘</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-info.cpp" line="444"/> ++ <location filename="../../libpeony-qt/file-info.cpp" line="530"/> + <source>folder</source> + <translation>文件夹</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-info.cpp" line="448"/> +- <location filename="../../libpeony-qt/file-info.cpp" line="456"/> +- <location filename="../../libpeony-qt/file-info.cpp" line="458"/> ++ <location filename="../../libpeony-qt/file-info.cpp" line="534"/> ++ <location filename="../../libpeony-qt/file-info.cpp" line="542"/> ++ <location filename="../../libpeony-qt/file-info.cpp" line="544"/> + <source>file</source> + <translation>文件</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-info.cpp" line="451"/> ++ <location filename="../../libpeony-qt/file-info.cpp" line="537"/> + <source>text file</source> + <translation>文本文件</translation> + </message> +@@ -2553,35 +2646,35 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <translation type="vanished">删除文件警告</translation> + </message> + <message> +- <location filename="../../libpeony-qt/model/file-item.cpp" line="251"/> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="259"/> + <source>Warning</source> + <translation>警告</translation> + </message> + <message> +- <location filename="../../libpeony-qt/model/file-item.cpp" line="253"/> +- <location filename="../../libpeony-qt/model/file-item.cpp" line="326"/> +- <location filename="../../libpeony-qt/model/file-item.cpp" line="338"/> +- <location filename="../../libpeony-qt/model/file-item.cpp" line="346"/> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="261"/> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="340"/> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="352"/> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="360"/> + <source>Error</source> + <translation>错误</translation> + </message> + <message> +- <location filename="../../libpeony-qt/model/file-item.cpp" line="313"/> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="327"/> + <source>Open Link failed</source> + <translation>打开链接失败</translation> + </message> + <message> +- <location filename="../../libpeony-qt/model/file-item.cpp" line="314"/> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="328"/> + <source>File not exist, do you want to delete the link file?</source> + <translation>目标文件夹不存在,是否删除该无效快捷方式?</translation> + </message> + <message> +- <location filename="../../libpeony-qt/model/file-item.cpp" line="327"/> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="341"/> + <source>Can not open path "%1",permission denied.</source> + <translation>打开路径 "%1" 失败,权限被拒绝。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/model/file-item.cpp" line="337"/> ++ <location filename="../../libpeony-qt/model/file-item.cpp" line="351"/> + <source>Can not find path "%1",are you moved or renamed it?</source> + <translation>未找到路径:"%1",您是否已经移动或者重命名?</translation> + </message> +@@ -2593,47 +2686,57 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <context> + <name>Peony::FileItemModel</name> + <message> +- <location filename="../../libpeony-qt/model/file-item-model.cpp" line="357"/> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="374"/> + <source>child(ren)</source> + <translation>个子项</translation> + </message> + <message> +- <location filename="../../libpeony-qt/model/file-item-model.cpp" line="344"/> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="361"/> + <source>Symbol Link, </source> + <translation>快捷方式,</translation> + </message> + <message> +- <location filename="../../libpeony-qt/model/file-item-model.cpp" line="403"/> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="295"/> ++ <source>System Disk</source> ++ <translation>系统盘</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="432"/> + <source>File Name</source> + <translation>文件名称</translation> + </message> + <message> +- <location filename="../../libpeony-qt/model/file-item-model.cpp" line="407"/> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="436"/> + <source>Delete Date</source> + <translation>删除日期</translation> + </message> + <message> +- <location filename="../../libpeony-qt/model/file-item-model.cpp" line="409"/> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="438"/> + <source>Create Date</source> + <translation>创建时间</translation> + </message> + <message> +- <location filename="../../libpeony-qt/model/file-item-model.cpp" line="416"/> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="445"/> + <source>File Size</source> + <translation>文件大小</translation> + </message> + <message> +- <location filename="../../libpeony-qt/model/file-item-model.cpp" line="418"/> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="448"/> + <source>Original Path</source> + <translation>原始路径</translation> + </message> + <message> +- <location filename="../../libpeony-qt/model/file-item-model.cpp" line="414"/> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="450"/> ++ <source>Path</source> ++ <translation>路径</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="443"/> + <source>File Type</source> + <translation>文件类型</translation> + </message> + <message> +- <location filename="../../libpeony-qt/model/file-item-model.cpp" line="412"/> ++ <location filename="../../libpeony-qt/model/file-item-model.cpp" line="441"/> + <source>Modified Date</source> + <translation>修改日期</translation> + </message> +@@ -2645,27 +2748,25 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <translation type="vanished">添加标记...</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="202"/> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="227"/> + <source>Add File Label</source> + <translation>添加标记</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="225"/> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="250"/> + <source>Delete All Label</source> + <translation>删除所有标记</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.h" line="104"/> + <source>Peony File Labels Menu Extension</source> +- <translation>文件标记</translation> ++ <translation type="vanished">文件标记</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.h" line="107"/> + <source>Tag a File with Menu.</source> +- <translation>菜单中增加标记功能.</translation> ++ <translation type="vanished">菜单中增加标记功能.</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="237"/> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="262"/> + <source>label management ...</source> + <translation>标识管理 ...</translation> + </message> +@@ -2673,7 +2774,7 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <context> + <name>Peony::FileLabelWidget</name> + <message> +- <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="422"/> ++ <location filename="../../libpeony-qt/controls/menu/menu-plugin-manager.cpp" line="447"/> + <source>label management ...</source> + <translation>标识管理 ...</translation> + </message> +@@ -2701,96 +2802,96 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <translation type="vanished">未设定用来打开文件“%1”的应用程序。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="146"/> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="162"/> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="177"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="154"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="170"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="185"/> + <source>The opening mode of the %1 %2</source> + <translation>%1%2 打开方式</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="146"/> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="147"/> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="177"/> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="178"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="154"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="155"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="185"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="186"/> + <source>unknown</source> + <translation>未知</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="147"/> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="178"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="155"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="186"/> + <source>No application is set to open file "%1 %2"</source> + <translation>未设定用来打开文件“%1%2”的应用程序。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="152"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="160"/> + <source>Still using the last opened application:</source> + <translation>仍使用最后打开的应用:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="162"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="170"/> + <source>known</source> + <translation>已知</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="165"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="173"/> + <source>Open application is used by default:</source> + <translation>默认使用打开的应用程序:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="186"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="194"/> + <source>You can search in the Software Center for an application that can open this file, or select an existing application on your computer.</source> + <translation>您可以在软件中心搜索能够打开此文件的应用程序,或者选取电脑上的现有应用程序。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="208"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="216"/> + <source>Other application:</source> + <translation>其他应用程序:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="210"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="218"/> + <source>Select application:</source> + <translation>选取应用程序:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="256"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="267"/> + <source>Always open the %1%2 file with this application</source> + <translation>始终用该应用打开%1%2文件</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="279"/> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="286"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="293"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="300"/> + <source>Choose other application</source> + <translation>选择其他应用</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="280"/> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="293"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="294"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="307"/> + <source>Go to application center</source> + <translation>去软件中心安装应用</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="351"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="365"/> + <source>Ok</source> + <translation>确定</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="352"/> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="421"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="366"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="444"/> + <source>Cancel</source> + <translation>取消</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="412"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="435"/> + <source>Desktop files(*.desktop)</source> + <translation>桌面文件(*.desktop)</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="419"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="442"/> + <source>Select Open Action</source> + <translation>选择打开方式</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="420"/> ++ <location filename="../../libpeony-qt/file-launcher/file-lauch-dialog.cpp" line="443"/> + <source>Select</source> + <translation>选择</translation> + </message> +@@ -2800,21 +2901,21 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <message> + <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="215"/> + <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="310"/> +- <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="498"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="520"/> + <source>Execute Directly</source> + <translation>直接运行</translation> + </message> + <message> + <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="216"/> + <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="311"/> +- <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="499"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="521"/> + <source>Execute in Terminal</source> + <translation>在终端运行</translation> + </message> + <message> + <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="219"/> + <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="315"/> +- <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="503"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="525"/> + <source>Detected launching an executable file %1, you want?</source> + <translation>正在打开一个可执行文件%1, 你希望?</translation> + </message> +@@ -2825,7 +2926,7 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <message> + <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="256"/> + <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="368"/> +- <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="537"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="559"/> + <source>Open Failed</source> + <translation>无法打开</translation> + </message> +@@ -2854,43 +2955,43 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + </message> + <message> + <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="309"/> +- <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="497"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="519"/> + <source>By Default App</source> + <translation>使用默认打开方式</translation> + </message> + <message> + <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="314"/> +- <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="502"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="524"/> + <source>Launch Options</source> + <translation>执行选项</translation> + </message> + <message> + <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="357"/> +- <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="526"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="548"/> + <source>Open Link failed</source> + <translation>打开链接失败</translation> + </message> + <message> + <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="358"/> +- <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="527"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="549"/> + <source>File not exist, do you want to delete the link file?</source> + <translation>目标文件不存在,您需要删除该链接吗?</translation> + </message> + <message> + <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="369"/> +- <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="538"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="560"/> + <source>Can not open %1, Please confirm you have the right authority.</source> + <translation>无法打开%1,请确认您有正确的打开权限。</translation> + </message> + <message> + <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="373"/> +- <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="543"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="565"/> + <source>Open App failed</source> + <translation>快捷方式存在问题</translation> + </message> + <message> + <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="374"/> +- <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="544"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="566"/> + <source>The linked app is changed or uninstalled, so it can not work correctly. + Do you want to delete the link file?</source> + <translation>该链接指向的应用已经被修改或者卸载,因此该快捷方式无法正常工作。 +@@ -2899,7 +3000,7 @@ Do you want to delete the link file?</source> + <message> + <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="388"/> + <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="393"/> +- <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="555"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="577"/> + <source>Error</source> + <translation>错误</translation> + </message> +@@ -2910,7 +3011,7 @@ Do you want to delete the link file?</source> + </message> + <message> + <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="393"/> +- <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="555"/> ++ <location filename="../../libpeony-qt/file-launcher/file-launch-action.cpp" line="577"/> + <source>Can not get a default application for opening %1, do you want open it with text format?</source> + <translation>没有找到默认打开%1的应用, 是否用文本编辑器打开?</translation> + </message> +@@ -2956,84 +3057,106 @@ Do you want to delete the link file?</source> + <translation type="vanished">文件创建</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="186"/> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="415"/> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="535"/> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="867"/> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1776"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="201"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="434"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="554"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="888"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1908"/> + <source>Move file error</source> + <translation>移动文件错误</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="302"/> + <source>File System</source> +- <translation>文件系统</translation> ++ <translation type="vanished">文件系统</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="304"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="318"/> + <source>Data</source> + <translation>数据盘</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="310"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="324"/> + <source>%1 no space left on device. Copy file size: %2 GB, Space needed: %3 GB.</source> + <translation>%1 设备可用空间不足,拷贝文件大小:%2 GB,需要空间:%3 GB。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="315"/> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1952"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="329"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1841"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1856"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="2085"/> + <source>File move error</source> + <translation>文件移动错误</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="831"/> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="841"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="852"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="862"/> + <source>Invalid move operation, cannot move a file into its sub directories.</source> + <translation>非法的移动操作,不能移动文件夹到自身路径下。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="885"/> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="916"/> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="969"/> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1239"/> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1263"/> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1396"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="911"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="942"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1010"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1300"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1324"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1468"/> + <source>The file name exceeds the limit</source> + <translation>文件名长度超出限制</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1216"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1272"/> + <source>Create file error</source> + <translation>创建文件错误</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1222"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1844"/> ++ <source>Can not move %1 to %2: Read-only mode, can not write-in</source> ++ <translation>无法移动 %1 到 %2:只读模式,无法写入</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1859"/> ++ <source>Can not move %1 to %2: Permission denied</source> ++ <translation>无法移动 %1 到 %2:权限不够</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="900"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1278"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1289"/> + <source>Cannot opening file, permission denied!</source> + <translation>无法打开文件,权限不够!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1224"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1280"/> + <source>File:%1 was not found.</source> + <translation>未找到文件:%1.</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="832"/> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1683"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="853"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1761"/> + <source>Invalid Operation.</source> + <translation>非法的操作.</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1743"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="316"/> ++ <source>System Disk</source> ++ <translation>系统盘</translation> ++ </message> ++ <message> ++ <source>open file %1 error: Read-only file system</source> ++ <translation type="vanished">打开文件 %1 出错:只读文件系统</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1874"/> + <source>File delete error</source> + <translation>文件删除错误</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1822"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1955"/> + <source>Link file error</source> + <translation>创建文件链接失败</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1954"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="2087"/> + <source>Burning does not support replacement</source> + <translation>刻录操作不支持替换文件</translation> + </message> +@@ -3046,8 +3169,8 @@ Do you want to delete the link file?</source> + <translation type="vanished">文件删除</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="839"/> +- <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1745"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="860"/> ++ <location filename="../../libpeony-qt/file-operation/file-move-operation.cpp" line="1876"/> + <source>Invalid Operation</source> + <translation>非法的操作</translation> + </message> +@@ -3206,25 +3329,29 @@ Do you want to delete the link file?</source> + <context> + <name>Peony::FileOperationErrorDialogNotSupported</name> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="320"/> + <source>Yes</source> +- <translation>是</translation> ++ <translation type="vanished">是</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="312"/> + <source>No</source> +- <translation>否</translation> ++ <translation type="vanished">否</translation> + </message> + <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="338"/> + <source>Cancel</source> +- <translation type="vanished">取消</translation> ++ <translation>取消</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="346"/> ++ <source>Delete</source> ++ <translation>删除</translation> + </message> + <message> + <source>Do the same</source> + <translation type="vanished">全部应用</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="357"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="383"/> + <source>Make sure the disk is not full or write protected and that the file is not protected</source> + <translation>请确认磁盘未满或未被写保护而且文件未被使用</translation> + </message> +@@ -3232,17 +3359,22 @@ Do you want to delete the link file?</source> + <context> + <name>Peony::FileOperationErrorDialogWarning</name> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="204"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="211"/> + <source>OK</source> + <translation>确定</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="213"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="220"/> + <source>Cancel</source> + <translation>取消</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="245"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="227"/> ++ <source>Skip all</source> ++ <translation>全部跳过</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="262"/> + <source>Make sure the disk is not full or write protected and that the file is not protected</source> + <translation>请确认磁盘未满或未被写保护而且文件未被使用</translation> + </message> +@@ -3254,11 +3386,51 @@ Do you want to delete the link file?</source> + <context> + <name>Peony::FileOperationInfo</name> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="1096"/> +- <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="1098"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="1151"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="1153"/> + <source>Symbolic Link</source> + <translation>快捷方式</translation> + </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="1220"/> ++ <source>Copy</source> ++ <translation>复制</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="1223"/> ++ <source>Move</source> ++ <translation>移动</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="1228"/> ++ <source>Rename</source> ++ <translation>重命名</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="1231"/> ++ <source>Link</source> ++ <translation>链接</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="1234"/> ++ <source>Delete</source> ++ <translation>删除</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="1237"/> ++ <source>Delete Permanently</source> ++ <translation>永久删除</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="1240"/> ++ <source>Restore</source> ++ <translation>还原</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="1245"/> ++ <source>New</source> ++ <translation>新建</translation> ++ </message> + <message> + <source> - Symbolic Link</source> + <translation type="vanished">-快捷方式</translation> +@@ -3271,12 +3443,12 @@ Do you want to delete the link file?</source> + <translation type="vanished">删除文件警告</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="262"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="265"/> + <source>Warn</source> + <translation>警告</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="262"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="265"/> + <source>'%1' is occupied,you cannot operate!</source> + <translation>“%1”已被占用,您无法进行操作!</translation> + </message> +@@ -3285,70 +3457,84 @@ Do you want to delete the link file?</source> + <translation type="vanished">否,跳转到设置</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="282"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="295"/> + <source>OK</source> + <translation>删除</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="286"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="299"/> + <source>Cancel</source> + <translation>取消</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="291"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="307"/> + <source>Do you want to put selected %1 item(s) into trash?</source> + <translation>确认要将选中的 %1 项放入回收站吗?</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="293"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="309"/> + <source>Do not show again</source> + <translation>不再显示</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="461"/> + <source>File System</source> +- <translation>文件系统</translation> ++ <translation type="vanished">文件系统</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="463"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="477"/> ++ <source>System Disk</source> ++ <translation>系统盘</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="479"/> + <source>Data</source> + <translation>数据盘</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="467"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="483"/> + <source>Insufficient storage space</source> + <translation>存储空间不足</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="470"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="486"/> + <source>%1 no space left on device. Copy file size: %2 GB, Space needed: %3 GB.</source> + <translation>%1 设备可用空间不足,拷贝文件大小:%2 GB,需要空间:%3 GB。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="512"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="528"/> + <source>Can't delete.</source> + <translation>不能删除</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="513"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="529"/> + <source>You can't delete a file whenthe file is doing another operation</source> + <translation>不能删除一个正在进行其它操作的文件</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="634"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="659"/> + <source>File Operation is Busy</source> + <translation>操作正忙</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="635"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="660"/> + <source>There have been one or more fileoperation(s) executing before. Youroperation will wait for executinguntil it/them done. If you really want to execute file operations parallelly anyway, you can change the default option "Allow Parallel" in option menu.</source> +- <translation>在执行该操作之前有操作未完成, 它需要等待上一个操作完成后再执行. 如果你希望文件操作并行, 你可以更改选项菜单中的"允许操作并行"配置.</translation> ++ <translation>在执行该操作之前有操作未完成,它需要等待上一个操作完成后再执行。如果你希望文件操作并行,你可以更改选项菜单中的“允许操作并行”配置。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="661"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="686"/> + <source>The long name file is saved to %1</source> + <translation>长文件被保存到%1</translation> + </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="1024"/> ++ <source>Undo %1</source> ++ <translation>撤销 %1</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-manager.cpp" line="1032"/> ++ <source>Redo %1</source> ++ <translation>重做 %1</translation> ++ </message> + <message> + <source>The system cannot hibernate or sleep</source> + <translation type="vanished">无法进入休眠或睡眠模式</translation> +@@ -3472,47 +3658,47 @@ Do you want to delete the link file?</source> + <translation type="vanished">文件名称:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="258"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="260"/> + <source>File Type:</source> + <translation>类型</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="286"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="292"/> + <source>Time Access:</source> + <translation>上次打开时间</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="279"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="284"/> + <source>Time Modified:</source> + <translation>修改时间</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="295"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="302"/> + <source>Children Count:</source> + <translation>包含文件</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="265"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="268"/> + <source>Size:</source> + <translation>大小</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="272"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="276"/> + <source>Time Created:</source> + <translation>创建时间</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="304"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="312"/> + <source>Image resolution:</source> + <translation>分辨率</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="311"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="320"/> + <source>color model:</source> + <translation>色彩模式</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="380"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="389"/> + <source>usershare</source> + <translation>本机共享</translation> + </message> +@@ -3525,14 +3711,14 @@ Do you want to delete the link file?</source> + <translation type="vanished">图片格式:</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="435"/> +- <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="436"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="444"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="445"/> + <source>%1x%2</source> + <translation>%1x%2</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="493"/> +- <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="494"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="502"/> ++ <location filename="../../libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp" line="503"/> + <source>%1 total, %2 hidden</source> + <translation>共%1项,其中%2个隐藏文件</translation> + </message> +@@ -3567,22 +3753,22 @@ Do you want to delete the link file?</source> + <translation type="vanished">文件重命名</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="76"/> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="82"/> + <source>File Rename error</source> + <translation>文件重命名错误</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="77"/> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="83"/> + <source>Invalid file name %1%2%3 .</source> + <translation>非法的文件名%1%2%3.</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="93"/> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="99"/> + <source>Are you sure to hidden this file?</source> + <translation>确定隐藏该文件?</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="126"/> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="132"/> + <source>When change the file suffix, the file may be invalid. Are you sure to change it ?</source> + <translation>改变扩展名可能导致文件不可用,确认更改吗?</translation> + </message> +@@ -3603,8 +3789,8 @@ Do you want to delete the link file?</source> + <translation type="vanished">文件名 "%1" 不合法</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="92"/> +- <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="125"/> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="98"/> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="131"/> + <source>File Rename warning</source> + <translation>文件重命名警告</translation> + </message> +@@ -3613,8 +3799,8 @@ Do you want to delete the link file?</source> + <translation type="vanished">文件 "%1" 将会被隐藏!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="217"/> +- <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="249"/> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="223"/> ++ <location filename="../../libpeony-qt/file-operation/file-rename-operation.cpp" line="255"/> + <source>Rename file error</source> + <translation>重命名文件错误</translation> + </message> +@@ -3622,19 +3808,29 @@ Do you want to delete the link file?</source> + <context> + <name>Peony::FileTrashOperation</name> + <message> +- <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="72"/> +- <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="95"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="61"/> ++ <source>File trash error</source> ++ <translation>刪除文件到回收站错误</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="63"/> ++ <source>Can not trash %1: Read-only file system</source> ++ <translation>无法删除 %1 到回收站:只读文件系统</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="117"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="140"/> + <source>trash:///</source> + <translation>trash:///</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="75"/> +- <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="98"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="120"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="143"/> + <source>Trash file error</source> + <translation>刪除文件到回收站错误</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="78"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="123"/> + <source>Invalid Operation! Can not trash "%1".</source> + <translation>非法的操作! 不能回收 "%1".</translation> + </message> +@@ -3647,7 +3843,7 @@ Do you want to delete the link file?</source> + <translation type="vanished">无法回收大于10G的文件,是否需要永久删除?</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="202"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="247"/> + <source>An unmanageable conflict exists. Please check the recycle bin.</source> + <translation>存在无法处理的冲突,请检查回收站。</translation> + </message> +@@ -3687,28 +3883,33 @@ Do you want to delete the link file?</source> + <source>Untrash file error</source> + <translation>从回收站恢复文件错误</translation> + </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-untrash-operation.cpp" line="404"/> ++ <source>Can not find trashed file %1, might be restored or removed.</source> ++ <translation>无法找到回收的文件 %1,文件可能被还原或者删除。</translation> ++ </message> + </context> + <context> + <name>Peony::GlobalSettings</name> + <message> +- <location filename="../../libpeony-qt/global-settings.cpp" line="104"/> +- <location filename="../../libpeony-qt/global-settings.cpp" line="554"/> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="167"/> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="800"/> + <source>yyyy/MM/dd</source> + <translation></translation> + </message> + <message> +- <location filename="../../libpeony-qt/global-settings.cpp" line="105"/> +- <location filename="../../libpeony-qt/global-settings.cpp" line="546"/> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="168"/> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="792"/> + <source>HH:mm:ss</source> + <translation></translation> + </message> + <message> +- <location filename="../../libpeony-qt/global-settings.cpp" line="543"/> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="789"/> + <source>AP hh:mm:ss</source> + <translation></translation> + </message> + <message> +- <location filename="../../libpeony-qt/global-settings.cpp" line="557"/> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="803"/> + <source>yyyy-MM-dd</source> + <translation></translation> + </message> +@@ -3716,17 +3917,17 @@ Do you want to delete the link file?</source> + <context> + <name>Peony::HeaderBar</name> + <message> +- <location filename="../../libpeony-qt/windows/properties-window.cpp" line="954"/> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="1047"/> + <source>Spread</source> + <translation>最大化</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/properties-window.cpp" line="955"/> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="1048"/> + <source>Minimize</source> + <translation>最小化</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/properties-window.cpp" line="956"/> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="1049"/> + <source>Close</source> + <translation>关闭</translation> + </message> +@@ -3791,18 +3992,17 @@ Do you want to delete the link file?</source> + <translation type="obsolete">计算机</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="407"/> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="409"/> + <source>Search "%1" in "%2"</source> + <translation>在%2中搜索%1</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="417"/> +- <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="446"/> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="419"/> + <source>File System</source> + <translation>文件系统</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="417"/> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="419"/> + <source>Search results for all files marked in "%1" in "%2"</source> + <translation>在“%2”中搜索所有“%1”标识文件的结果</translation> + </message> +@@ -3811,12 +4011,12 @@ Do you want to delete the link file?</source> + <translation type="vanished">拷贝路径(&C)</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="538"/> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="525"/> + <source>Open In New Tab</source> + <translation>在新标签页中打开</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="542"/> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="529"/> + <source>Open In New Window</source> + <translation>在新窗口中打开</translation> + </message> +@@ -3829,7 +4029,7 @@ Do you want to delete the link file?</source> + <translation type="vanished">在新窗口中打开(&N)</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="536"/> ++ <location filename="../../libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp" line="523"/> + <source>Copy Directory</source> + <translation>拷贝路径</translation> + </message> +@@ -3837,12 +4037,13 @@ Do you want to delete the link file?</source> + <context> + <name>Peony::MountOperation</name> + <message> +- <location filename="../../libpeony-qt/mount-operation.cpp" line="93"/> ++ <location filename="../../libpeony-qt/mount-operation.cpp" line="101"/> ++ <location filename="../../libpeony-qt/mount-operation.cpp" line="239"/> + <source>Operation Cancelled</source> + <translation>操作被取消</translation> + </message> + <message> +- <location filename="../../libpeony-qt/mount-operation.cpp" line="190"/> ++ <location filename="../../libpeony-qt/mount-operation.cpp" line="199"/> + <source>Login failed, unknown username or password error, please re-enter!</source> + <translation>验证失败,未知的用户名或者密码错误,请重新输入!</translation> + </message> +@@ -3883,27 +4084,27 @@ Do you want to delete the link file?</source> + <context> + <name>Peony::NewFileLaunchDialog</name> + <message> +- <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="246"/> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="244"/> + <source>Choose new application</source> + <translation>选择一个新的应用</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="248"/> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="246"/> + <source>Choose an Application to open this file</source> + <translation>选择一个应用打开这个文件</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="255"/> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="253"/> + <source>apply now</source> + <translation>立即应用</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="261"/> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="260"/> + <source>OK</source> + <translation>确定</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="262"/> ++ <location filename="../../libpeony-qt/controls/property-page/open-with-properties-page.cpp" line="264"/> + <source>Cancel</source> + <translation>取消</translation> + </message> +@@ -4086,55 +4287,93 @@ Do you want to delete the link file?</source> + <translation type="vanished">用户</translation> + </message> + </context> ++<context> ++ <name>Peony::PropertiesSetDialog</name> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1477"/> ++ <source>Confirming property settings</source> ++ <translation>确认属性设置</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1485"/> ++ <source>Whether to apply to the current selected option or to selected options and subfolders and subfiles.</source> ++ <translation>是否应用于当前所选项,还是应用于所选项以及子文件夹和子文件。</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1496"/> ++ <source>Apply the current selection</source> ++ <translation>应用于当前所选项</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1497"/> ++ <source>Apply the current selection as well as subfolders and subfiles</source> ++ <translation>应用于当前所选项以及子文件夹和子文件</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1520"/> ++ <source>Ok</source> ++ <translation>确定</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/property-page/basic-properties-page.cpp" line="1521"/> ++ <source>Cancel</source> ++ <translation>取消</translation> ++ </message> ++</context> + <context> + <name>Peony::PropertiesWindow</name> + <message> +- <location filename="../../libpeony-qt/windows/properties-window.cpp" line="345"/> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="349"/> + <source>Trash</source> + <translation>回收站</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/properties-window.cpp" line="349"/> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="353"/> + <source>Recent</source> + <translation>最近</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/properties-window.cpp" line="357"/> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="361"/> + <source>Selected</source> + <translation>选中</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/properties-window.cpp" line="357"/> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="361"/> + <source> %1 Files</source> + <translation>%1 个文件</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/properties-window.cpp" line="363"/> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="367"/> + <source>usershare</source> + <translation>本机共享</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/properties-window.cpp" line="374"/> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="406"/> + <source>Data</source> + <translation>数据盘</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/properties-window.cpp" line="381"/> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="411"/> ++ <source>System Disk</source> ++ <translation>系统盘</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="417"/> + <source>Properties</source> + <translation>属性</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/properties-window.cpp" line="481"/> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="545"/> + <source>Ok</source> + <translation>确定</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/properties-window.cpp" line="482"/> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="546"/> + <source>Cancel</source> + <translation>取消</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/properties-window.cpp" line="490"/> ++ <location filename="../../libpeony-qt/windows/properties-window.cpp" line="556"/> + <source>Restore</source> + <translation>还原</translation> + </message> +@@ -4209,47 +4448,53 @@ Do you want to delete the link file?</source> + <translation type="vanished">选择文件类型</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.cpp" line="259"/> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.cpp" line="117"/> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.cpp" line="274"/> ++ <source>Search File</source> ++ <translation>搜索文件</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.cpp" line="233"/> + <source>Clear</source> + <translation>清空</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="120"/> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="125"/> + <source>all</source> + <translation>全部</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="120"/> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="125"/> + <source>file folder</source> + <translation>文件夹</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="120"/> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="125"/> + <source>image</source> + <translation>图片</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="121"/> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="126"/> + <source>video</source> + <translation>视频</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="121"/> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="126"/> + <source>text file</source> + <translation>文本</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="121"/> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="126"/> + <source>audio</source> + <translation>音频</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="121"/> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="126"/> + <source>others</source> + <translation>其它</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="121"/> ++ <location filename="../../libpeony-qt/controls/tool-bar/search-bar-container.h" line="126"/> + <source>wps file</source> + <translation>WPS文件</translation> + </message> +@@ -4300,15 +4545,19 @@ Do you want to delete the link file?</source> + </message> + <message> + <location filename="../../libpeony-qt/model/side-bar-favorite-item.cpp" line="95"/> +- <source>Quick access</source> ++ <source>Quick Access</source> + <translation>快速访问</translation> + </message> ++ <message> ++ <source>Quick access</source> ++ <translation type="vanished">快速访问</translation> ++ </message> + <message> + <source>Favorite</source> + <translation type="vanished">快速访问</translation> + </message> + <message> +- <location filename="../../libpeony-qt/model/side-bar-favorite-item.cpp" line="189"/> ++ <location filename="../../libpeony-qt/model/side-bar-favorite-item.cpp" line="190"/> + <source>KmreData</source> + <translation>移动应用</translation> + </message> +@@ -4344,39 +4593,39 @@ Do you want to delete the link file?</source> + <translation type="vanished">属性(&R)</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="68"/> +- <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="91"/> +- <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="117"/> +- <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="136"/> +- <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="317"/> +- <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="371"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="69"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="92"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="118"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="138"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="339"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="394"/> + <source>Properties</source> + <translation>属性</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="102"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="103"/> + <source>Delete Symbolic</source> + <translation>删除</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="188"/> +- <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="365"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="209"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="388"/> + <source>Unmount</source> + <translation>卸载</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="197"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="218"/> + <source>Eject</source> + <translation>弹出</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="225"/> +- <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="258"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="246"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="280"/> + <source>Format</source> + <translation>格式化</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="294"/> ++ <location filename="../../libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp" line="316"/> + <source>burndata</source> + <translation></translation> + </message> +@@ -4458,9 +4707,13 @@ Do you want to delete the link file?</source> + <context> + <name>Peony::SyncThread</name> + <message> +- <location filename="../../libpeony-qt/sync-thread.cpp" line="66"/> + <source>notify</source> + <translatorcomment>温馨提示</translatorcomment> ++ <translation type="vanished">温馨提示</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/sync-thread.cpp" line="66"/> ++ <source>Notify</source> + <translation>温馨提示</translation> + </message> + </context> +@@ -4664,6 +4917,44 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + 版权所有(C): 2019,天津麒麟信息技术有限公司.</translation> + </message> + </context> ++<context> ++ <name>Peony::TooltipsManager</name> ++ <message> ++ <location filename="../../libpeony-qt/tooltips-manager.cpp" line="92"/> ++ <location filename="../../libpeony-qt/tooltips-manager.cpp" line="235"/> ++ <source>Unknow</source> ++ <translation>未知</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/tooltips-manager.cpp" line="175"/> ++ <location filename="../../libpeony-qt/tooltips-manager.cpp" line="204"/> ++ <location filename="../../libpeony-qt/tooltips-manager.cpp" line="234"/> ++ <source>Type: </source> ++ <translation>类型:</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/tooltips-manager.cpp" line="180"/> ++ <source>Duration:</source> ++ <translation>时长:</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/tooltips-manager.cpp" line="182"/> ++ <location filename="../../libpeony-qt/tooltips-manager.cpp" line="212"/> ++ <location filename="../../libpeony-qt/tooltips-manager.cpp" line="239"/> ++ <source>Size: </source> ++ <translation>大小:</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/tooltips-manager.cpp" line="210"/> ++ <source>Res: </source> ++ <translation>分辨率:</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/tooltips-manager.cpp" line="238"/> ++ <source>Mod Date: </source> ++ <translation>修改日期:</translation> ++ </message> ++</context> + <context> + <name>Peony::UserShareInfoManager</name> + <message> +@@ -4684,32 +4975,32 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <context> + <name>ProgressBar</name> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="860"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="906"/> + <source>starting ...</source> + <translation>正在开始 ...</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="957"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="1006"/> + <source>canceling ...</source> + <translation>取消中 ...</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="959"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="1008"/> + <source>sync ...</source> + <translation>正在同步...</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="1014"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="1063"/> + <source>continue</source> + <translation>继续</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="1016"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="1065"/> + <source>pause</source> + <translation>暂停</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="1023"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar.cpp" line="1072"/> + <source>close</source> + <translation>关闭</translation> + </message> +@@ -4799,19 +5090,25 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <translation type="vanished">无法回收大于10G的文件,是否需要永久删除?</translation> + </message> + <message> +- <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="148"/> +- <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="108"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="165"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="153"/> + <source>The file is too large to be moved to the recycle bin. Do you want to permanently delete it?</source> + <translation>文件过大,不可移入回收站。是否永久删除该文件?</translation> + </message> + <message> +- <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="152"/> +- <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="111"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="169"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="156"/> + <source>These files are too large to be moved to the recycle bin. Do you want to permanently delete these %1 files?</source> + <translation>文件过大,不可移入回收站。是否永久删除这 %1 项文件?</translation> + </message> + <message> +- <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="321"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="186"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="462"/> ++ <source>Delete</source> ++ <translation>删除</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="346"/> + <source>Clean the Trash</source> + <translation>清空回收站</translation> + </message> +@@ -4820,7 +5117,7 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <translation type="vanished">清空回收站</translation> + </message> + <message> +- <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="333"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="358"/> + <source>Do you want to empty the recycle bin and delete the files permanently? Once it has begun there is no way to restore them.</source> + <translation>确认要清空回收站内的文件吗?此操作无法撤销。</translation> + </message> +@@ -4869,42 +5166,42 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <translation type="vanished">请检查设备是否正在使用,确认没有使用后再次弹出</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="544"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="592"/> + <source>Format failed</source> + <translation>格式化失败</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="546"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="594"/> + <source>YES</source> + <translation>确认</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1033"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1086"/> + <source>Formatting successful! But failed to set the device name.</source> + <translation>格式化成功!设备名设置失败。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1050"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1103"/> + <source>qmesg_notify</source> + <translation>通知</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1065"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1118"/> + <source>Format</source> + <translation>格式化</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1067"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1120"/> + <source>Begin Format</source> + <translation>开始</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1070"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1123"/> + <source>Close</source> + <translation>关闭</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1031"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1084"/> + <source>Format operation has been finished successfully.</source> + <translation>格式化操作已成功完成。</translation> + </message> +@@ -4913,18 +5210,18 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <translation type="vanished">格式化成功!设备名设置失败。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1050"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1103"/> + <source>Sorry, the format operation is failed!</source> + <translation>很遗憾,格式化操作失败了,您可以重新试下!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1063"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1116"/> + <source>Formatting this volume will erase all data on it. Please backup all retained data before formatting. Do you want to continue ?</source> + <translation>格式化此卷将清除其上的所有数据。请在格式化之前备份所有保留的数据。您想继续吗?</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1031"/> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1033"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1084"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1086"/> + <source>format</source> + <translation>格式化</translation> + </message> +@@ -4937,27 +5234,36 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + <translation type="vanished">关闭</translation> + </message> + <message> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="673"/> + <location filename="../../libpeony-qt/sync-thread.cpp" line="63"/> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="950"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="1003"/> + <source>File Manager</source> + <translation>文件管理器</translation> + </message> + <message> +- <location filename="../../libpeony-qt/vfs/search-vfs-register.h" line="40"/> ++ <location filename="../../libpeony-qt/global-settings.cpp" line="676"/> ++ <source>Notify</source> ++ <translation>温馨提示</translation> ++ </message> ++ <message> ++ <source>notify</source> ++ <translation type="vanished">温馨提示</translation> ++ </message> ++ <message> + <source>Default search vfs of peony</source> +- <translation>默认文件搜索</translation> ++ <translation type="vanished">默认文件搜索</translation> + </message> + <message> + <location filename="../../libpeony-qt/volumeManager.cpp" line="172"/> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1752"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1782"/> + <source>Force unmount failed</source> + <translation>强制卸载失败</translation> + </message> + <message> + <location filename="../../libpeony-qt/model/side-bar-net-work-item.cpp" line="136"/> + <location filename="../../libpeony-qt/volumeManager.cpp" line="172"/> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1752"/> +- <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="542"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1782"/> ++ <location filename="../../libpeony-qt/windows/format_dialog.cpp" line="590"/> + <source>Error: %1 + </source> + <translation>错误: %1 +@@ -4965,39 +5271,41 @@ Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.</source> + </message> + <message> + <location filename="../../libpeony-qt/model/side-bar-net-work-item.cpp" line="142"/> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1755"/> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1805"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1785"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1835"/> + <source>Data synchronization is complete,the device has been unmount successfully!</source> + <translation>数据同步完成,设备已经成功卸载!</translation> + </message> + <message> + <location filename="../../libpeony-qt/model/side-bar-net-work-item.cpp" line="131"/> + <location filename="../../libpeony-qt/model/side-bar-net-work-item.cpp" line="136"/> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1784"/> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1787"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1814"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1817"/> + <source>Unmount failed</source> + <translation>卸载失败</translation> + </message> + <message> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1772"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1802"/> + <source>Not authorized to perform operation.</source> + <translation>操作未获得授权。</translation> + </message> + <message> + <location filename="../../libpeony-qt/model/side-bar-net-work-item.cpp" line="131"/> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1784"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1814"/> + <source>Unable to unmount it, you may need to close some programs, such as: GParted etc.</source> + <translation>无法卸载,您可能需要先关闭一些程序,如分区编辑器等。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1787"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1817"/> + <source>Error: %1 + Do you want to unmount forcely?</source> + <translation>错误: %1 + 是否强制卸载?</translation> + </message> + <message> +- <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="326"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="187"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="351"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="463"/> + <source>Cancel</source> + <translation>取消</translation> + </message> +@@ -5006,29 +5314,29 @@ Do you want to unmount forcely?</source> + <translation type="vanished">无论如何弹出</translation> + </message> + <message> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1245"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1272"/> + <source>Failed to activate device: Incorrect passphrase</source> + <translation>无法激活设备: 错误的口令</translation> + </message> + <message> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1255"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1282"/> + <source>The device has been mount successfully!</source> + <translation>设备挂载成功!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1450"/> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1488"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1477"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1515"/> + <source>Eject device failed, the reason may be that the device has been removed, etc.</source> + <translation>弹出设备失败,可能是设备已经被移除等原因。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1456"/> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1495"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1483"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1522"/> + <source>Data synchronization is complete and the device can be safely unplugged!</source> + <translation>数据同步完成,设备可以安全拔出!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1991"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="2020"/> + <source>Password is empty, please re-enter!</source> + <translation>密码为空,请重新输入!</translation> + </message> +@@ -5041,9 +5349,9 @@ Do you want to unmount forcely?</source> + <translation type="vanished">文件管理器通知</translation> + </message> + <message> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1452"/> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1490"/> +- <location filename="../../libpeony-qt/volumeManager.cpp" line="1772"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1479"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1517"/> ++ <location filename="../../libpeony-qt/volumeManager.cpp" line="1802"/> + <source>Eject failed</source> + <translation>弹出失败</translation> + </message> +@@ -5060,26 +5368,26 @@ Do you want to unmount forcely?</source> + <message> + <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="304"/> + <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="309"/> +- <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="382"/> +- <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="387"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="405"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="410"/> + <source>File is not existed.</source> + <translation>文件不存在.</translation> + </message> + <message> + <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="317"/> +- <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="395"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="418"/> + <source>Share Data</source> + <translation>本机共享</translation> + </message> + <message> + <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="321"/> +- <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="399"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="422"/> + <source>Trash</source> + <translation>回收站</translation> + </message> + <message> + <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="325"/> +- <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="403"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="426"/> + <source>Recent</source> + <translation>最近</translation> + </message> +@@ -5088,7 +5396,7 @@ Do you want to unmount forcely?</source> + <location filename="../../libpeony-qt/vfs/custom-vfs-file.cpp" line="428"/> + <location filename="../../libpeony-qt/vfs/custom-vfs-file.cpp" line="462"/> + <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="371"/> +- <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="250"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="252"/> + <source>Operation not supported</source> + <translation>操作不支持</translation> + </message> +@@ -5120,8 +5428,8 @@ Do you want to unmount forcely?</source> + <message> + <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="624"/> + <location filename="../../libpeony-qt/vfs/favorite-vfs-file.cpp" line="632"/> +- <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="231"/> +- <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="267"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="233"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="269"/> + <source>The virtual file system cannot be opened</source> + <translation>虚拟文件系统无法打开</translation> + </message> +@@ -5191,15 +5499,17 @@ Do you want to unmount forcely?</source> + <translation type="vanished">没有发现该文件</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="539"/> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="556"/> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="572"/> +- <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="866"/> +- <location filename="../../libpeony-qt/file-utils.cpp" line="162"/> +- <location filename="../../libpeony-qt/file-utils.cpp" line="184"/> +- <location filename="../../libpeony-qt/file-utils.cpp" line="206"/> +- <location filename="../../libpeony-qt/file-utils.cpp" line="213"/> +- <location filename="../../libpeony-qt/file-utils.cpp" line="229"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="579"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="596"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="612"/> ++ <location filename="../../libpeony-qt/file-operation/file-copy-operation.cpp" line="911"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="167"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="189"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="211"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="218"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="228"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="244"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="258"/> + <source>duplicate</source> + <translation>副本</translation> + </message> +@@ -5208,13 +5518,18 @@ Do you want to unmount forcely?</source> + <translation type="vanished">获取文件信息时出现错误:没有目标文件。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-utils.cpp" line="353"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="384"/> + <source>data</source> + <translation>数据盘</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-utils.cpp" line="371"/> +- <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="345"/> ++ <location filename="../../libpeony-qt/file-utils.cpp" line="387"/> ++ <location filename="../../libpeony-qt/model/side-bar-file-system-item.cpp" line="257"/> ++ <source>System Disk</source> ++ <translation>系统盘</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="365"/> + <source>label</source> + <translation>标记</translation> + </message> +@@ -5225,10 +5540,8 @@ Do you want to unmount forcely?</source> + <translation>计算机</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-utils.cpp" line="356"/> +- <location filename="../../libpeony-qt/model/side-bar-file-system-item.cpp" line="257"/> + <source>File System</source> +- <translation>文件系统</translation> ++ <translation type="vanished">文件系统</translation> + </message> + <message> + <location filename="../../libpeony-qt/model/side-bar-file-system-item.cpp" line="262"/> +@@ -5236,12 +5549,12 @@ Do you want to unmount forcely?</source> + <translation>数据盘</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="411"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="437"/> + <source>Failed to open file "%1": insufficient permissions.</source> + <translation>打开文件"%1"失败:权限不足。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="422"/> ++ <location filename="../../libpeony-qt/file-operation/file-operation-error-dialogs.cpp" line="448"/> + <source>File “%1” does not exist. Please check whether the file has been deleted.</source> + <translation>文件“%1”不存在,请检查文件是否被删除。</translation> + </message> +@@ -5256,24 +5569,24 @@ Do you want to unmount forcely?</source> + <translation>被占用!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1298"/> +- <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="156"/> +- <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="388"/> +- <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="182"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1389"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="173"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="449"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="227"/> + <source>Are you sure you want to permanently delete this file? Once deletion begins, the file will not be recoverable.</source> + <translation>确定永久删除该文件吗?一旦开始删除,文件将不可恢复。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1302"/> +- <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="160"/> +- <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="392"/> +- <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="186"/> ++ <location filename="../../libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp" line="1393"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="177"/> ++ <location filename="../../libpeony-qt/convenient-utils/file-operation-utils.cpp" line="453"/> ++ <location filename="../../libpeony-qt/file-operation/file-trash-operation.cpp" line="231"/> + <source>Are you sure you want to permanently delete these %1 files? Once deletion begins, these file will not be recoverable.</source> + <translation>确定永久删除这 %2 项文件吗?一旦开始删除,文件将不可恢复。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="283"/> +- <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="416"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="285"/> ++ <location filename="../../libpeony-qt/vfs/label-vfs-file.cpp" line="439"/> + <source>Virtual file directories do not support move operations</source> + <translation>虚拟文件路径不支持移动和复制操作</translation> + </message> +@@ -5285,17 +5598,17 @@ Do you want to unmount forcely?</source> + <message> + <location filename="../../libpeony-qt/vfs/custom-vfs-register.h" line="73"/> + <source>Default custom vfs info of peony</source> +- <translation type="unfinished"></translation> ++ <translation></translation> + </message> + <message> + <location filename="../../libpeony-qt/vfs/custom-vfs-register.h" line="115"/> + <source>Default local vfs info of peony</source> +- <translation type="unfinished"></translation> ++ <translation></translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/properties-window-factory.h" line="61"/> ++ <location filename="../../libpeony-qt/windows/properties-window-factory.h" line="62"/> + <source>Show properties plugin window.</source> +- <translation type="unfinished"></translation> ++ <translation></translation> + </message> + </context> + <context> +@@ -5392,8 +5705,8 @@ Do you want to unmount forcely?</source> + <name>UdfBurn::UdfFormatDialog</name> + <message> + <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="47"/> +- <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="166"/> +- <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="217"/> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="168"/> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="219"/> + <source>Format</source> + <translation>格式化</translation> + </message> +@@ -5413,57 +5726,57 @@ Do you want to unmount forcely?</source> + <translation>确定</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="84"/> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="86"/> + <source>Cancel</source> + <translation>取消</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="92"/> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="94"/> + <source>Unknown</source> + <translation>未知</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="131"/> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="133"/> + <source>Warning</source> + <translation>警告</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="131"/> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="133"/> + <source>The disc name cannot be set to empty, please re-enter it!</source> + <translation>设备名称不能设置为空,请重新输入!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="166"/> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="168"/> + <source>Format operation has been finished successfully.</source> + <translation>格式化操作已成功完成。</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="171"/> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="173"/> + <source>Sorry, the format operation is failed!</source> + <translation>很遗憾,格式化操作失败了,您可以重新试下!</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="172"/> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="174"/> + <source>Failed</source> + <translation>失败</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="206"/> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="208"/> + <source>Formatting. Do not close this window</source> + <translation>正在格式化, 请勿关闭</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="216"/> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="218"/> + <source>Formatting this disc will erase all data on it. Please backup all retained data before formatting. Do you want to continue ?</source> + <translation>格式化此光盘将擦除其上的所有数据。 请在格式化前备份所有保留的数据。 你想继续吗 ?</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="219"/> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="221"/> + <source>Begin Format</source> + <translation>开始</translation> + </message> + <message> +- <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="220"/> ++ <location filename="../../libpeony-qt/windows/ky-udf-format-dialog.cpp" line="222"/> + <source>Close</source> + <translation>关闭</translation> + </message> +@@ -5548,4 +5861,36 @@ Do you want to unmount forcely?</source> + <translation>关闭</translation> + </message> + </context> ++<context> ++ <name>progressBarHelper</name> ++ <message> ++ <source>Time is being calculated</source> ++ <translation type="vanished">正在计算时间</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar-helper.cpp" line="32"/> ++ <source>Calculating time</source> ++ <translation>正在计算时间</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar-helper.cpp" line="38"/> ++ <source>%1day%2hrs%3mins%4sec</source> ++ <translation>%1天%2小时%3分钟%4秒</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar-helper.cpp" line="44"/> ++ <source>%1hrs%2mins%3sec</source> ++ <translation>%1小时%2分钟%3秒</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar-helper.cpp" line="49"/> ++ <source>%1 mins%2sec</source> ++ <translation>%1 分钟%2秒</translation> ++ </message> ++ <message> ++ <location filename="../../libpeony-qt/file-operation/file-operation-progress-bar-helper.cpp" line="53"/> ++ <source>%1sec</source> ++ <translation>%1秒</translation> ++ </message> ++</context> + </TS> +diff --git a/translations/peony-qt-desktop/peony-qt-desktop_zh_CN.ts b/translations/peony-qt-desktop/peony-qt-desktop_zh_CN.ts +index 884502e..fd82304 100644 +--- a/translations/peony-qt-desktop/peony-qt-desktop_zh_CN.ts ++++ b/translations/peony-qt-desktop/peony-qt-desktop_zh_CN.ts +@@ -9,7 +9,7 @@ + <translation>桌面图标视图</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="669"/> ++ <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="674"/> + <source>New Folder</source> + <translation>新建文件夹</translation> + </message> +@@ -22,27 +22,27 @@ + <translation type="vanished">删除文件警告</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="1031"/> ++ <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="1036"/> + <source>Open failed</source> + <translation>打开失败</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="1032"/> ++ <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="1037"/> + <source>Open directory failed, you have no permission!</source> + <translation>打开文件夹失败,您没有该目录的权限!</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="1014"/> ++ <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="1019"/> + <source>Open Link failed</source> + <translation>打开快捷方式失败</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="763"/> ++ <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="768"/> + <source>Set Background</source> + <translation>设置背景</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="1015"/> ++ <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="1020"/> + <source>File not exist, do you want to delete the link file?</source> + <translation>目标文件夹不存在,是否删除该无效快捷方式?</translation> + </message> +@@ -433,7 +433,7 @@ + <translation type="vanished">关闭桌面并退出</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="226"/> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="229"/> + <source>peony-qt-desktop</source> + <translation>桌面</translation> + </message> +@@ -446,30 +446,40 @@ + <translation type="vanished">桌面</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="560"/> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="573"/> + <source>Close the peony desktop window</source> + <translation>关闭桌面程序</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="563"/> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="576"/> + <source>Take over the dbus service.</source> + <translation>接管DBus服务。</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="566"/> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="579"/> + <source>Take over the desktop displaying</source> + <translation>接管桌面</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="569"/> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="582"/> + <source>Setup backgrounds</source> + <translation>Setup backgrounds</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="572"/> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="585"/> + <source>Clear standard icons</source> + <translation>Clear standard icons</translation> + </message> ++ <message> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="872"/> ++ <source>Failed to get screen config</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="872"/> ++ <source>Error message is: %1. Using fallback config to setup desktop.</source> ++ <translation type="unfinished"></translation> ++ </message> + <message> + <source>Open learning center.</source> + <translation type="vanished">打开学习中心</translation> +@@ -498,12 +508,12 @@ + <translation type="vanished">错误</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktopbackgroundwindow.cpp" line="100"/> ++ <location filename="../../peony-qt-desktop/desktopbackgroundwindow.cpp" line="105"/> + <source>Set Background</source> + <translation>设置背景</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktopbackgroundwindow.cpp" line="105"/> ++ <location filename="../../peony-qt-desktop/desktopbackgroundwindow.cpp" line="110"/> + <source>Display Settings</source> + <translation>显示设置</translation> + </message> +diff --git a/translations/peony-qt/peony-qt_zh_CN.ts b/translations/peony-qt/peony-qt_zh_CN.ts +index bcac88c..0095f7c 100644 +--- a/translations/peony-qt/peony-qt_zh_CN.ts ++++ b/translations/peony-qt/peony-qt_zh_CN.ts +@@ -83,12 +83,22 @@ p, li { white-space: pre-wrap; } + <context> + <name>FileLabelBox</name> + <message> +- <location filename="../../src/control/file-label-box.cpp" line="72"/> ++ <location filename="../../src/control/file-label-box.cpp" line="69"/> ++ <source>Open In New Window</source> ++ <translation type="unfinished">在新窗口中打开</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/file-label-box.cpp" line="78"/> ++ <source>Open In New Tab</source> ++ <translation type="unfinished">在新标签页中打开</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/file-label-box.cpp" line="87"/> + <source>Rename</source> + <translation>重命名</translation> + </message> + <message> +- <location filename="../../src/control/file-label-box.cpp" line="77"/> ++ <location filename="../../src/control/file-label-box.cpp" line="92"/> + <source>Edit Color</source> + <translation>编辑颜色</translation> + </message> +@@ -136,12 +146,12 @@ p, li { white-space: pre-wrap; } + <translation>排序类型</translation> + </message> + <message> +- <location filename="../../src/control/header-bar.cpp" line="356"/> ++ <location filename="../../src/control/header-bar.cpp" line="358"/> + <source>Option</source> + <translation>选项</translation> + </message> + <message> +- <location filename="../../src/control/header-bar.cpp" line="472"/> ++ <location filename="../../src/control/header-bar.cpp" line="474"/> + <source>Operate Tips</source> + <translation>操作提示</translation> + </message> +@@ -155,55 +165,55 @@ p, li { white-space: pre-wrap; } + <translation type="vanished">详情</translation> + </message> + <message> +- <location filename="../../src/control/header-bar.cpp" line="369"/> ++ <location filename="../../src/control/header-bar.cpp" line="371"/> + <source>&Copy</source> + <translation>复制</translation> + </message> + <message> +- <location filename="../../src/control/header-bar.cpp" line="372"/> ++ <location filename="../../src/control/header-bar.cpp" line="374"/> + <source>Copy</source> + <translation>复制</translation> + </message> + <message> +- <location filename="../../src/control/header-bar.cpp" line="384"/> ++ <location filename="../../src/control/header-bar.cpp" line="386"/> + <source>&Cut</source> + <translation>剪切</translation> + </message> + <message> +- <location filename="../../src/control/header-bar.cpp" line="387"/> ++ <location filename="../../src/control/header-bar.cpp" line="389"/> + <source>Cut</source> + <translation>剪切</translation> + </message> + <message> +- <location filename="../../src/control/header-bar.cpp" line="393"/> ++ <location filename="../../src/control/header-bar.cpp" line="395"/> + <source>&Select All</source> + <translation>全选</translation> + </message> + <message> +- <location filename="../../src/control/header-bar.cpp" line="397"/> +- <location filename="../../src/control/header-bar.cpp" line="409"/> ++ <location filename="../../src/control/header-bar.cpp" line="399"/> ++ <location filename="../../src/control/header-bar.cpp" line="411"/> + <source>Select All</source> + <translation>全选</translation> + </message> + <message> +- <location filename="../../src/control/header-bar.cpp" line="414"/> ++ <location filename="../../src/control/header-bar.cpp" line="416"/> + <location filename="../../src/control/header-bar.cpp" line="926"/> + <location filename="../../src/control/header-bar.cpp" line="944"/> + <source>Deselect All</source> + <translation>取消全选</translation> + </message> + <message> +- <location filename="../../src/control/header-bar.cpp" line="419"/> ++ <location filename="../../src/control/header-bar.cpp" line="421"/> + <source>&Delete to trash</source> + <translation>删除到回收站</translation> + </message> + <message> +- <location filename="../../src/control/header-bar.cpp" line="422"/> ++ <location filename="../../src/control/header-bar.cpp" line="424"/> + <source>Delete to trash</source> + <translation>删除到回收站</translation> + </message> + <message> +- <location filename="../../src/control/header-bar.cpp" line="473"/> ++ <location filename="../../src/control/header-bar.cpp" line="475"/> + <source>Don't find any terminal, please install at least one terminal!</source> + <translation>没有找到任何终端插件,请确认您至少安装了一个!</translation> + </message> +@@ -329,32 +339,32 @@ p, li { white-space: pre-wrap; } + <context> + <name>MainWindow</name> + <message> +- <location filename="../../src/windows/main-window.cpp" line="889"/> ++ <location filename="../../src/windows/main-window.cpp" line="939"/> + <source>File Manager</source> + <translation>文件管理器</translation> + </message> + <message> +- <location filename="../../src/windows/main-window.cpp" line="411"/> ++ <location filename="../../src/windows/main-window.cpp" line="418"/> + <source>Undo</source> + <translation>撤销</translation> + </message> + <message> +- <location filename="../../src/windows/main-window.cpp" line="418"/> ++ <location filename="../../src/windows/main-window.cpp" line="425"/> + <source>Redo</source> + <translation>重做</translation> + </message> + <message> +- <location filename="../../src/windows/main-window.cpp" line="790"/> ++ <location filename="../../src/windows/main-window.cpp" line="837"/> + <source>warn</source> + <translation>警告</translation> + </message> + <message> +- <location filename="../../src/windows/main-window.cpp" line="790"/> ++ <location filename="../../src/windows/main-window.cpp" line="837"/> + <source>This operation is not supported.</source> + <translation>不支持此操作。</translation> + </message> + <message> +- <location filename="../../src/windows/main-window.cpp" line="887"/> ++ <location filename="../../src/windows/main-window.cpp" line="937"/> + <source>Search</source> + <translation>搜索</translation> + </message> +@@ -383,7 +393,7 @@ p, li { white-space: pre-wrap; } + <translation type="vanished">文件管理器</translation> + </message> + <message> +- <location filename="../../src/windows/main-window.cpp" line="902"/> ++ <location filename="../../src/windows/main-window.cpp" line="952"/> + <source>New Folder</source> + <translation>新建文件夹</translation> + </message> +@@ -399,50 +409,50 @@ p, li { white-space: pre-wrap; } + <translation type="vanished">在新窗口中打开(&N)</translation> + </message> + <message> +- <location filename="../../src/control/navigation-side-bar.cpp" line="590"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="645"/> + <source>warn</source> + <translation>警告</translation> + </message> + <message> +- <location filename="../../src/control/navigation-side-bar.cpp" line="590"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="645"/> + <source>This operation is not supported.</source> + <translation>不支持此操作。</translation> + </message> + <message> +- <location filename="../../src/control/navigation-side-bar.cpp" line="224"/> +- <location filename="../../src/control/navigation-side-bar.cpp" line="628"/> +- <location filename="../../src/control/navigation-side-bar.cpp" line="646"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="253"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="683"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="701"/> + <source>Tips</source> + <translation>提示</translation> + </message> + <message> +- <location filename="../../src/control/navigation-side-bar.cpp" line="224"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="253"/> + <source>The device is in busy state, please perform this operation later.</source> + <translation>设备正忙, 请稍后执行此操作.</translation> + </message> + <message> +- <location filename="../../src/control/navigation-side-bar.cpp" line="628"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="683"/> + <source>This is an abnormal Udisk, please fix it or format it</source> + <translation>这是个异常U盘,请将其修复或格式化</translation> + </message> + <message> +- <location filename="../../src/control/navigation-side-bar.cpp" line="646"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="701"/> + <source>This is an empty drive, please insert a Disc.</source> + <translation>这是一个空光驱, 请插入光盘.</translation> + </message> + <message> +- <location filename="../../src/control/navigation-side-bar.cpp" line="259"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="293"/> + <source>Open In New Window</source> + <translation>在新窗口中打开</translation> + </message> + <message> +- <location filename="../../src/control/navigation-side-bar.cpp" line="275"/> + <location filename="../../src/control/navigation-side-bar.cpp" line="309"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="343"/> + <source>Can not open %1, %2</source> + <translation>无法打开%1, %2</translation> + </message> + <message> +- <location filename="../../src/control/navigation-side-bar.cpp" line="293"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="327"/> + <source>Open In New Tab</source> + <translation>在新标签页中打开</translation> + </message> +@@ -458,12 +468,12 @@ p, li { white-space: pre-wrap; } + <translation type="vanished">所有标记...</translation> + </message> + <message> +- <location filename="../../src/control/navigation-side-bar.cpp" line="848"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="903"/> + <source>Manager tags...</source> + <translation>标识模式</translation> + </message> + <message> +- <location filename="../../src/control/navigation-side-bar.cpp" line="851"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="906"/> + <source>More tags...</source> + <translation>更多标识</translation> + </message> +@@ -475,9 +485,8 @@ p, li { white-space: pre-wrap; } + <translation type="vanished">计算机</translation> + </message> + <message> +- <location filename="../../src/control/navigation-tab-bar.cpp" line="131"/> + <source>Search "%1" in "%2"</source> +- <translation>在%2中搜索%1</translation> ++ <translation type="vanished">在%2中搜索%1</translation> + </message> + </context> + <context> +@@ -487,103 +496,113 @@ p, li { white-space: pre-wrap; } + <translation type="vanished">高级搜索</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="78"/> ++ <location filename="../../src/control/operation-menu.cpp" line="83"/> + <source>Keep Allow</source> + <translation>置顶窗口</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="90"/> ++ <location filename="../../src/control/operation-menu.cpp" line="95"/> + <source>Show Hidden</source> + <translation>显示隐藏文件</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="98"/> ++ <location filename="../../src/control/operation-menu.cpp" line="103"/> + <source>Show File Extension</source> + <translation>显示文件扩展名</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="103"/> ++ <location filename="../../src/control/operation-menu.cpp" line="108"/> + <source>Show Create Time</source> + <translation>显示创建时间</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="110"/> ++ <location filename="../../src/control/operation-menu.cpp" line="115"/> + <source>Show Relative Time</source> + <translation>显示相对时间</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="123"/> ++ <location filename="../../src/control/operation-menu.cpp" line="128"/> + <source>Forbid thumbnailing</source> + <translation>禁用缩略图</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="133"/> ++ <location filename="../../src/control/operation-menu.cpp" line="138"/> + <source>Resident in Backend</source> + <translation>常驻后台</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="142"/> ++ <location filename="../../src/control/operation-menu.cpp" line="147"/> + <source>Parallel Operations</source> + <translation>允许操作并行</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="148"/> ++ <location filename="../../src/control/operation-menu.cpp" line="153"/> + <source>Set samba password</source> + <translation>设置samba共享密码</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="162"/> ++ <location filename="../../src/control/operation-menu.cpp" line="193"/> + <source>Tips</source> + <translation>提示</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="162"/> ++ <location filename="../../src/control/operation-menu.cpp" line="193"/> + <source>The user already has a samba password, do you need to reset the samba password?</source> + <translation>用户已经设置了samba共享密码, 是否希望重新设置?</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="173"/> ++ <location filename="../../src/control/operation-menu.cpp" line="204"/> + <source>Samba set user password</source> + <translation>设置samba共享密码</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="171"/> ++ <location filename="../../src/control/operation-menu.cpp" line="202"/> + <source>Samba password:</source> + <translation>共享密码:</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="181"/> +- <location filename="../../src/control/operation-menu.cpp" line="192"/> ++ <location filename="../../src/control/operation-menu.cpp" line="212"/> ++ <location filename="../../src/control/operation-menu.cpp" line="223"/> + <source>Warning</source> + <translation>警告</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="181"/> ++ <location filename="../../src/control/operation-menu.cpp" line="212"/> + <source>Samba set password failed, Please re-enter!</source> + <translation>设置共享密码失败, 请重新输入</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="192"/> ++ <location filename="../../src/control/operation-menu.cpp" line="223"/> + <source>Shared configuration service exception, please confirm if there is an ongoing shared configuration operation, or please reset the share!</source> + <translation>共享设置服务异常, 请确认是否已经有正在进行的共享设置操作, 或者重新设置共享!</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="201"/> ++ <location filename="../../src/control/operation-menu.cpp" line="232"/> + <source>Open each folder in a new window</source> + <translation>在新窗口打开文件夹</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="207"/> ++ <location filename="../../src/control/operation-menu.cpp" line="238"/> + <source>Plugin manager Settings</source> + <translation>插件管理设置</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="216"/> ++ <location filename="../../src/control/operation-menu.cpp" line="244"/> ++ <source>Show Network</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="249"/> ++ <source>Connect to Server</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/operation-menu.cpp" line="261"/> + <source>Help</source> + <translation>帮助</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="220"/> ++ <location filename="../../src/control/operation-menu.cpp" line="265"/> + <source>About</source> + <translation>关于</translation> + </message> +@@ -591,27 +610,27 @@ p, li { white-space: pre-wrap; } + <context> + <name>OperationMenuEditWidget</name> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="284"/> ++ <location filename="../../src/control/operation-menu.cpp" line="338"/> + <source>Edit</source> + <translation>编辑</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="295"/> ++ <location filename="../../src/control/operation-menu.cpp" line="349"/> + <source>copy</source> + <translation>复制</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="304"/> ++ <location filename="../../src/control/operation-menu.cpp" line="358"/> + <source>paste</source> + <translation>粘贴</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="313"/> ++ <location filename="../../src/control/operation-menu.cpp" line="367"/> + <source>cut</source> + <translation>剪切</translation> + </message> + <message> +- <location filename="../../src/control/operation-menu.cpp" line="322"/> ++ <location filename="../../src/control/operation-menu.cpp" line="376"/> + <source>trash</source> + <translation>删除</translation> + </message> +@@ -724,9 +743,8 @@ p, li { white-space: pre-wrap; } + <context> + <name>Peony::SearchWidget</name> + <message> +- <location filename="../../src/control/search-widget.cpp" line="50"/> + <source>Search</source> +- <translation>搜索</translation> ++ <translation type="vanished">搜索</translation> + </message> + </context> + <context> +@@ -787,12 +805,17 @@ p, li { white-space: pre-wrap; } + 2.你使用的系统主题不是qt默认支持的主题,并且你没有安装相关的平台插件。如果你正在使用Gtk主题作为系统主题,尝试安装qt5-gtk2-platformtheme以解决此问题。</translation> + </message> + <message> +- <location filename="../../src/peony-application.cpp" line="495"/> ++ <location filename="../../src/peony-application.cpp" line="338"/> ++ <source>Peony is disabled to start !</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/peony-application.cpp" line="510"/> + <source>Peony Qt</source> + <translation>文件管理器</translation> + </message> + <message> +- <location filename="../../src/peony-application.cpp" line="496"/> ++ <location filename="../../src/peony-application.cpp" line="511"/> + <source>Author: + Yue Lan <lanyue@kylinos.cn> + Meihong He <hemeihong@kylinos.cn> +@@ -975,32 +998,37 @@ Do you want to unmount forcely?</source> + <translation>原始路径</translation> + </message> + <message> +- <location filename="../../src/control/sort-type-menu.cpp" line="82"/> ++ <location filename="../../src/control/sort-type-menu.cpp" line="55"/> ++ <source>Path</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/sort-type-menu.cpp" line="86"/> + <source>Use current sorting for all folders</source> + <translation>所有目录使用当前排序</translation> + </message> + <message> +- <location filename="../../src/control/sort-type-menu.cpp" line="102"/> ++ <location filename="../../src/control/sort-type-menu.cpp" line="111"/> + <source>By %1</source> + <translation>按%1</translation> + </message> + <message> +- <location filename="../../src/control/sort-type-menu.cpp" line="111"/> ++ <location filename="../../src/control/sort-type-menu.cpp" line="120"/> + <source>Newest to oldest</source> + <translation>从新到旧</translation> + </message> + <message> +- <location filename="../../src/control/sort-type-menu.cpp" line="112"/> ++ <location filename="../../src/control/sort-type-menu.cpp" line="121"/> + <source>Oldest to newest</source> + <translation>从旧到新</translation> + </message> + <message> +- <location filename="../../src/control/sort-type-menu.cpp" line="114"/> ++ <location filename="../../src/control/sort-type-menu.cpp" line="123"/> + <source>Files from large to small</source> + <translation>从大到小</translation> + </message> + <message> +- <location filename="../../src/control/sort-type-menu.cpp" line="115"/> ++ <location filename="../../src/control/sort-type-menu.cpp" line="124"/> + <source>Files from small to large</source> + <translation>从小到大</translation> + </message> +@@ -1023,14 +1051,14 @@ Do you want to unmount forcely?</source> + <translation type="vanished">修改日期</translation> + </message> + <message> +- <location filename="../../src/control/sort-type-menu.cpp" line="71"/> +- <location filename="../../src/control/sort-type-menu.cpp" line="118"/> ++ <location filename="../../src/control/sort-type-menu.cpp" line="75"/> ++ <location filename="../../src/control/sort-type-menu.cpp" line="127"/> + <source>Ascending</source> + <translation>升序</translation> + </message> + <message> +- <location filename="../../src/control/sort-type-menu.cpp" line="66"/> +- <location filename="../../src/control/sort-type-menu.cpp" line="117"/> ++ <location filename="../../src/control/sort-type-menu.cpp" line="70"/> ++ <location filename="../../src/control/sort-type-menu.cpp" line="126"/> + <source>Descending</source> + <translation>降序</translation> + </message> +@@ -1110,25 +1138,60 @@ Do you want to unmount forcely?</source> + <context> + <name>TabWidget</name> + <message> +- <location filename="../../src/control/tab-widget.cpp" line="285"/> ++ <location filename="../../src/control/tab-widget.cpp" line="297"/> + <source>Trash</source> + <translation>回收站</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.cpp" line="289"/> ++ <location filename="../../src/control/tab-widget.cpp" line="301"/> + <source>Clear</source> + <translation>清空</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.cpp" line="294"/> ++ <location filename="../../src/control/tab-widget.cpp" line="306"/> + <source>Recover</source> + <translation>还原</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.cpp" line="451"/> ++ <location filename="../../src/control/tab-widget.cpp" line="532"/> ++ <source>Search Settings</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="532"/> ++ <source>After the creation of the index, the next search can get the results of the document content containing the search term, during which you can exit the page at any time, we will continue to complete the creation in the background.</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="556"/> + <source>Computer</source> + <translation>计算机</translation> + </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="581"/> ++ <source>Condition</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="584"/> ++ <source>File Type</source> ++ <translation type="unfinished">文件类型</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="588"/> ++ <source>Modify time</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="592"/> ++ <source>File Size</source> ++ <translation type="unfinished">文件大小</translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.cpp" line="595"/> ++ <source>File Label</source> ++ <translation type="unfinished"></translation> ++ </message> + <message> + <source>Close Filter.</source> + <translation type="vanished">关闭筛选。</translation> +@@ -1138,32 +1201,32 @@ Do you want to unmount forcely?</source> + <translation type="vanished">筛选</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.cpp" line="526"/> ++ <location filename="../../src/control/tab-widget.cpp" line="711"/> + <source>Select Path</source> + <translation>选择路径</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.cpp" line="1347"/> ++ <location filename="../../src/control/tab-widget.cpp" line="1260"/> + <source>Warning</source> + <translation>警告</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.cpp" line="1349"/> ++ <location filename="../../src/control/tab-widget.cpp" line="1262"/> + <source>Error</source> + <translation>错误</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.cpp" line="1708"/> ++ <location filename="../../src/control/tab-widget.cpp" line="1633"/> + <source>Opening such files is not currently supported</source> + <translation>暂时不支持打开此类文件</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.cpp" line="1722"/> ++ <location filename="../../src/control/tab-widget.cpp" line="1648"/> + <source>Open failed</source> + <translation>打开失败</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.cpp" line="1723"/> ++ <location filename="../../src/control/tab-widget.cpp" line="1649"/> + <source>Open directory failed, you have no permission!</source> + <translation>打开文件夹失败,您没有该目录的权限!</translation> + </message> +@@ -1172,7 +1235,7 @@ Do you want to unmount forcely?</source> + <translation type="vanished">关闭高级搜索。</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.cpp" line="441"/> ++ <location filename="../../src/control/tab-widget.cpp" line="505"/> + <source>Search</source> + <translation>搜索</translation> + </message> +@@ -1197,13 +1260,11 @@ Do you want to unmount forcely?</source> + <translation type="vanished">选择路径</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.cpp" line="586"/> +- <location filename="../../src/control/tab-widget.cpp" line="746"/> + <source>is</source> +- <translation>是</translation> ++ <translation type="vanished">是</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.cpp" line="621"/> ++ <location filename="../../src/control/tab-widget.cpp" line="604"/> + <source>Please input key words...</source> + <translation>请输入关键词...</translation> + </message> +@@ -1212,149 +1273,154 @@ Do you want to unmount forcely?</source> + <translation type="vanished">请输入关键词...</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.cpp" line="707"/> +- <location filename="../../src/control/tab-widget.cpp" line="730"/> + <source>contains</source> +- <translation>包含</translation> ++ <translation type="vanished">包含</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="311"/> ++ <location filename="../../src/control/tab-widget.h" line="312"/> + <source>name</source> + <translation>名称</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="311"/> ++ <location filename="../../src/control/tab-widget.h" line="312"/> + <source>type</source> + <translation>类型</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="311"/> ++ <location filename="../../src/control/tab-widget.h" line="312"/> + <source>modify time</source> + <translation>修改时间</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="311"/> ++ <location filename="../../src/control/tab-widget.h" line="312"/> + <source>file size</source> + <translation>文件大小</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="312"/> +- <location filename="../../src/control/tab-widget.h" line="314"/> +- <location filename="../../src/control/tab-widget.h" line="315"/> + <source>all</source> +- <translation>全部</translation> ++ <translation type="vanished">全部</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="312"/> ++ <location filename="../../src/control/tab-widget.h" line="313"/> + <source>file folder</source> + <translation>文件夹</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="312"/> ++ <location filename="../../src/control/tab-widget.h" line="313"/> + <source>image</source> + <translation>图片</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="312"/> ++ <location filename="../../src/control/tab-widget.h" line="313"/> + <source>video</source> + <translation>视频</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="313"/> ++ <location filename="../../src/control/tab-widget.h" line="314"/> + <source>text file</source> + <translation>文本文档</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="313"/> ++ <location filename="../../src/control/tab-widget.h" line="314"/> + <source>audio</source> + <translation>音频</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="313"/> ++ <location filename="../../src/control/tab-widget.h" line="314"/> + <source>others</source> + <translation>其他</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="313"/> ++ <location filename="../../src/control/tab-widget.h" line="314"/> + <source>wps file</source> + <translation>WPS文件</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="314"/> ++ <location filename="../../src/control/tab-widget.h" line="315"/> + <source>today</source> + <translation>今天</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="314"/> ++ <location filename="../../src/control/tab-widget.h" line="315"/> + <source>this week</source> + <translation>本周</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="314"/> ++ <location filename="../../src/control/tab-widget.h" line="315"/> + <source>this month</source> + <translation>本月</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="314"/> ++ <location filename="../../src/control/tab-widget.h" line="315"/> + <source>this year</source> + <translation>今年</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="314"/> ++ <location filename="../../src/control/tab-widget.h" line="315"/> + <source>yesterday</source> + <translation>昨天</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="314"/> ++ <location filename="../../src/control/tab-widget.h" line="315"/> + <source>last week</source> + <translation>上周</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="314"/> ++ <location filename="../../src/control/tab-widget.h" line="315"/> + <source>last month</source> + <translation>上月</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="314"/> ++ <location filename="../../src/control/tab-widget.h" line="315"/> + <source>last year</source> + <translation>去年</translation> + </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="317"/> ++ <source>file name and content</source> ++ <translation type="unfinished"></translation> ++ </message> ++ <message> ++ <location filename="../../src/control/tab-widget.h" line="317"/> ++ <source>file name</source> ++ <translation type="unfinished"></translation> ++ </message> + <message> + <source>year ago</source> + <translation type="vanished">一年前</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="315"/> ++ <location filename="../../src/control/tab-widget.h" line="316"/> + <source>tiny(0-16K)</source> + <translation>极小(0-16K)</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="315"/> ++ <location filename="../../src/control/tab-widget.h" line="316"/> + <source>small(16k-1M)</source> + <translation>很小(16k-1M)</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="315"/> ++ <location filename="../../src/control/tab-widget.h" line="316"/> + <source>empty(0K)</source> + <translation>空(0K)</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="315"/> ++ <location filename="../../src/control/tab-widget.h" line="316"/> + <source>medium(1M-128M)</source> + <translation>中等(1M-128M)</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="315"/> ++ <location filename="../../src/control/tab-widget.h" line="316"/> + <source>big(128M-1G)</source> + <translation>大(128M-1G)</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="315"/> ++ <location filename="../../src/control/tab-widget.h" line="316"/> + <source>large(1-4G)</source> + <translation>巨大(1-4G)</translation> + </message> + <message> +- <location filename="../../src/control/tab-widget.h" line="315"/> ++ <location filename="../../src/control/tab-widget.h" line="316"/> + <source>great(>4G)</source> + <translation>极大(>4G)</translation> + </message> +@@ -1374,7 +1440,7 @@ Do you want to unmount forcely?</source> + <context> + <name>TitleLabel</name> + <message> +- <location filename="../../src/control/navigation-side-bar.cpp" line="989"/> ++ <location filename="../../src/control/navigation-side-bar.cpp" line="1041"/> + <source>Peony</source> + <translation>文件管理器</translation> + </message> diff -Nru peony-4.10.0.5/debian/patches/0091-update-changelog.patch peony-4.10.0.5/debian/patches/0091-update-changelog.patch --- peony-4.10.0.5/debian/patches/0091-update-changelog.patch 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/patches/0091-update-changelog.patch 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,8471 @@ +From: Yue-Lan <lanyue@kylinos.cn> +Date: Thu, 27 Feb 2025 17:28:11 +0800 +Subject: update changelog. + +--- + peony-qt-desktop/advanced-desktop-icon-view.cpp | 2735 ++++++++++++++++++++ + peony-qt-desktop/advanced-desktop-icon-view.h | 361 +++ + peony-qt-desktop/advanced-desktop-item-model.cpp | 1036 ++++++++ + peony-qt-desktop/advanced-desktop-item-model.h | 204 ++ + peony-qt-desktop/common.h | 40 + + peony-qt-desktop/desktop-background-manager.cpp | 78 +- + peony-qt-desktop/desktop-background-manager.h | 4 + + peony-qt-desktop/desktop-icon-view-delegate.cpp | 267 +- + peony-qt-desktop/desktop-icon-view-delegate.h | 17 +- + peony-qt-desktop/desktop-icon-view.cpp | 43 +- + peony-qt-desktop/desktop-icon-view.h | 0 + peony-qt-desktop/desktop-index-widget.cpp | 188 +- + peony-qt-desktop/desktop-item-model.cpp | 42 +- + peony-qt-desktop/desktop-item-model.h | 2 + + peony-qt-desktop/desktop-item-proxy-model.cpp | 112 +- + peony-qt-desktop/desktop-item-proxy-model.h | 22 +- + peony-qt-desktop/desktop-menu-plugin-manager.cpp | 5 + + peony-qt-desktop/desktop-menu.cpp | 62 +- + peony-qt-desktop/desktop-menu.h | 6 +- + peony-qt-desktop/desktop-window-manager.cpp | 487 ++++ + peony-qt-desktop/desktop-window-manager.h | 85 + + peony-qt-desktop/desktopbackgroundwindow.cpp | 183 +- + peony-qt-desktop/desktopbackgroundwindow.h | 27 +- + peony-qt-desktop/main.cpp | 7 + + peony-qt-desktop/peony-dbus-service.cpp | 4 +- + peony-qt-desktop/peony-dbus-service.h | 8 +- + peony-qt-desktop/peony-desktop-application.cpp | 641 ++--- + peony-qt-desktop/peony-desktop-application.h | 41 +- + peony-qt-desktop/peony-qt-desktop.pro | 13 +- + peony-qt-desktop/user-dir-manager.cpp | 18 +- + peony-qt-desktop/user-dir-manager.h | 2 +- + .../peony-qt-desktop/peony-qt-desktop_zh_CN.ts | 170 +- + 32 files changed, 5842 insertions(+), 1068 deletions(-) + create mode 100644 peony-qt-desktop/advanced-desktop-icon-view.cpp + create mode 100644 peony-qt-desktop/advanced-desktop-icon-view.h + create mode 100644 peony-qt-desktop/advanced-desktop-item-model.cpp + create mode 100644 peony-qt-desktop/advanced-desktop-item-model.h + create mode 100644 peony-qt-desktop/common.h + mode change 100755 => 100644 peony-qt-desktop/desktop-icon-view.cpp + mode change 100755 => 100644 peony-qt-desktop/desktop-icon-view.h + create mode 100644 peony-qt-desktop/desktop-window-manager.cpp + create mode 100644 peony-qt-desktop/desktop-window-manager.h + +diff --git a/peony-qt-desktop/advanced-desktop-icon-view.cpp b/peony-qt-desktop/advanced-desktop-icon-view.cpp +new file mode 100644 +index 0000000..f505d1a +--- /dev/null ++++ b/peony-qt-desktop/advanced-desktop-icon-view.cpp +@@ -0,0 +1,2735 @@ ++/* ++ * Peony-Qt ++ * ++ * Copyright (C) 2020, KylinSoft Co., Ltd. ++ * ++ * 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 3 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, see <https://www.gnu.org/licenses/>. ++ * ++ * Authors: yangyanwei <yangyanwei@kylinos.cn> ++ * ++ */ ++ ++#include <QApplication> ++#include <QPaintEvent> ++#include <QPainter> ++#include <QStyledItemDelegate> ++#include <QRubberBand> ++#include <QMouseEvent> ++#include <QStandardItem> ++#include <QDebug> ++#include <QMessageBox> ++#include <QClipboard> ++#include <QDrag> ++#include <QtX11Extras/QX11Info> ++#include <kstartupinfo.h> ++#include <QProcess> ++#include <QToolTip> ++#include <QWindow> ++#include <QDrag> ++ ++#include "advanced-desktop-icon-view.h" ++#include "file-enumerator.h" ++#include "file-meta-info.h" ++#include "file-info.h" ++#include "file-info-job.h" ++#include "file-info-manager.h" ++#include "file-watcher.h" ++#include "file-operation-manager.h" ++#include "file-move-operation.h" ++#include "file-trash-operation.h" ++#include "file-copy-operation.h" ++#include "file-operation-utils.h" ++#include "file-utils.h" ++ ++#include "thumbnail-manager.h" ++#include "usershare-manager.h" ++ ++#include "global-settings.h" ++#include "sound-effect.h" ++#include "audio-play-manager.h" ++ ++#include "desktop-item-proxy-model.h" ++#include "advanced-desktop-item-model.h" ++#include "peony-desktop-application.h" ++ ++#include "icon-view-style.h" ++#include "desktop-icon-view-delegate.h" ++#include "clipboard-utils.h" ++ ++#include "properties-window.h" ++ ++#include "desktop-menu.h" ++ ++#include "file-item-model.h" ++#include "file-launch-manager.h" ++ ++#include "desktop-index-widget.h" ++#include "common.h" ++ ++using namespace Peony; ++ ++static bool refreshing = false; ++ ++static bool meetSpecialConditions(const QStringList& selectedUris) ++{ ++ /* The desktop home directory, computer, and trash do not allow operations such as copying, cutting, ++ * deleting, renaming, moving, or using shortcut keys for corresponding operations.add by 2021/06/17 */ ++ static QString homeUri = "file://" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation); ++ ++ if (selectedUris.contains("computer:///") ++ ||selectedUris.contains("trash:///") ++ ||selectedUris.contains(homeUri)){ ++ return true; ++ } ++ ++ return false; ++} ++ ++AdvancedDesktopIconView::AdvancedDesktopIconView(QWidget *parent) : QAbstractItemView(parent) ++{ ++ ++ QString localeName = QLocale::system().name(); ++ if (localeName.contains("ug") || localeName.contains("kk") || localeName.contains("ky")) { ++ setLayoutDirection(Qt::RightToLeft); ++ } ++ ++ setAttribute(Qt::WA_AlwaysStackOnTop); ++ setFrameShape(QFrame::NoFrame); ++ setStyleSheet("QAbstractItemView { background-color: transparent; }"); ++ setItemDelegate(new DesktopIconViewDelegate(this)); ++ ++ auto zoomLevel = this->zoomLevel(); ++ setDefaultZoomLevel(zoomLevel); ++ ++ initShoutCut(); ++ initDoubleClick(); ++ ++ connect(qApp, &QApplication::paletteChanged, this, [=]() { ++ viewport()->update(); ++ }); ++ ++ auto globalSettings = Peony::GlobalSettings::getInstance(); ++ QString widgetThemeName = globalSettings->getValue("widgetThemeName").toString(); ++ if (widgetThemeName.contains("classical")) { ++ m_radius = 0; ++ } else { ++ m_radius = 6; ++ } ++ connect(globalSettings, &GlobalSettings::valueChanged, this, [=](const QString &key){ ++ if (key == "widgetThemeName") { ++ QString widgetThemeName = globalSettings->getValue("widgetThemeName").toString(); ++ if (widgetThemeName.contains("classical")) { ++ m_radius = 0; ++ } else { ++ m_radius = 6; ++ } ++ viewport()->update(); ++ } ++ }); ++ ++ m_edit_trigger_timer.setSingleShot(true); ++ m_edit_trigger_timer.setInterval(3000); ++ m_last_index = QModelIndex(); ++ ++ // rubberband ++ m_rubberBand = new QRubberBand(QRubberBand::Rectangle, this/*->viewport()*/); ++ m_rubberBand->setVisible(false); ++ ++ m_model = PeonyDesktopApplication::getModel(); ++ m_proxy_model = new DesktopItemProxyModel(m_model); ++ ++ m_proxy_model->setSourceModel(m_model); ++ ++ setModel(m_proxy_model); ++ setSelectionMode(QAbstractItemView::ExtendedSelection); ++ setDragDropMode(QAbstractItemView::DragDrop); ++ setMouseTracking(true);//追踪鼠标 ++ setEditTriggers(QListView::NoEditTriggers); ++ ++ // dnd ++ setDefaultDropAction(Qt::MoveAction); ++ ++ m_peonyDbusSer = new PeonyDbusService(this); ++ m_peonyDbusSer->DbusServerRegister(); ++ ++ setMouseTracking(true);//追踪鼠标 ++ ++ connect(this, &AdvancedDesktopIconView::updateView, this, [=]() { ++ m_itemPosHash.clear(); ++ m_autoArrange.clear(); ++ m_resolutionItemPosHash.clear(); ++ m_proxy_model->invalidate(); ++ ++ reset(); ++ if (m_proxy_model && m_proxy_model->getDesktopUseAutoLayout()) { ++ if (m_autoArrange.count() != m_proxy_model->rowCount()) { ++ qInfo()<<"desktop use auto layout but config cache is invalid, use current layout"; ++ auto sortedUris = layoutItems(); ++// relayoutExsitingItemsAndUpdate(sortedUris); ++ } ++ return; ++ } ++ resolutionChange(); ++ checkItemsOver(); ++ repaint(); ++ }); ++ ++ connect(m_model, &AdvancedDesktopItemModel::refreshed, this, [=]() { ++ this->setCursor(QCursor(Qt::ArrowCursor)); ++ refreshing = false; ++ if (m_proxy_model->getDesktopUseAutoLayout()) { ++ if (m_autoArrange.count() != m_proxy_model->rowCount()) { ++ qInfo()<<"desktop use auto layout but config cache is invalid, use current layout"; ++ auto sortedUris = layoutItems(); ++ m_autoArrange = sortedUris; ++ } ++ viewport()->update(); ++ return; ++ } ++ checkItemsOver(); ++ viewport()->update(); ++ }); ++ ++ // 图标和字体共同决定网格 ++ connect(this, &AdvancedDesktopIconView::iconSizeChanged, this, &AdvancedDesktopIconView::recalculateAvailableRowAndColumnCount); ++ ++ connect(qApp, &QApplication::fontChanged, this, [=](){ ++ //直接调用槽函数,获取font的高度是没改变之前的 ++ QTimer::singleShot(0, this, [this]() { ++ recalculateAvailableRowAndColumnCount(); ++ }); ++ }); ++ ++ connect(m_model, &AdvancedDesktopItemModel::requestClearIndexWidget, this, &AdvancedDesktopIconView::clearAllIndexWidgets); ++ ++ connect(GlobalSettings::getInstance(), &GlobalSettings::valueChanged, this, [=] (const QString &key) { ++ if (key == DISPLAY_STANDARD_ICONS || ++ key == HOME_ICON_VISIBLE || ++ key == TRASH_ICON_VISIBLE || ++ key == COMPUTER_ICON_VISIBLE) { ++ //this->refresh(); ++ //m_proxy_model->invalidate(); ++ m_proxy_model->invalidateModel(); ++ this->resolutionChange(); ++ checkItemsOver(); ++ } else if (SHOW_HIDDEN_PREFERENCE == key) { ++ m_show_hidden= GlobalSettings::getInstance()->getValue(key).toBool(); ++ } ++ }); ++ ++ ++ //fix task bar overlap with desktop icon and can drag move issue ++ //bug #27811,33188 ++ if (QGSettings::isSchemaInstalled(PANEL_SETTINGS)) ++ { ++ //panel monitor ++ if (!m_panelSetting) ++ m_panelSetting = new QGSettings(PANEL_SETTINGS, QByteArray(), this); ++ connect(m_panelSetting, &QGSettings::changed, this, [=](const QString &key){ ++ if (key == "panelposition" || key == "panelsize" || key == "settingsislandposition" || key == "paneltype") { ++ setMargins(); ++ recalculateAvailableRowAndColumnCount(); ++ } ++ }); ++ setMargins(); ++ } ++ ++ // try fixing #63358 ++ if (QGSettings::isSchemaInstalled(UKUI_STYLE_SETTINGS)) { ++ auto styleSettings = new QGSettings(UKUI_STYLE_SETTINGS, QByteArray(), this); ++ connect(styleSettings, &QGSettings::changed, this, [=](const QString &key){ ++ if (key == "iconThemeName") { ++ QTimer::singleShot(1000, viewport(), [=]{ ++ viewport()->update(); ++ }); ++ } ++ }); ++ } ++ connect(m_model, &AdvancedDesktopItemModel::sig_relayoutItems, this, [=](){ ++ if (m_proxy_model->getDesktopUseAutoLayout() && !m_is_renaming) { ++ auto list = m_autoArrange; ++ relayoutExsitingItemsAndUpdate(list); ++ } ++ }); ++ ++ connect(m_model, &AdvancedDesktopItemModel::selectUri, this, [=](const QString &uri){ ++ if (!m_itemPosHash.contains(uri)) { ++ return; ++ } ++ QTimer::singleShot(100, this, [=]() { ++ setSelections(QStringList() << uri); ++ scrollToSelection(uri); ++ setFocus(); ++ }); ++ }); ++ ++ connect(this, &AdvancedDesktopIconView::sig_fileCreated, this, &AdvancedDesktopIconView::fileCreated); ++ ++ bool desktopAutoLayout = GlobalSettings::getInstance()->getValue(DESKTOP_USE_AUTO_LAYOUT).toBool(); ++ m_proxy_model->setDesktopUseAutoLayout(desktopAutoLayout); ++ setDropIndicatorShown(desktopAutoLayout); ++ connect(GlobalSettings::getInstance(), &GlobalSettings::valueChanged, this, [=](const QString &key){ ++ if (key == DESKTOP_USE_AUTO_LAYOUT) { ++ bool desktopAutoLayout = GlobalSettings::getInstance()->getValue(DESKTOP_USE_AUTO_LAYOUT).toBool(); ++ m_proxy_model->setDesktopUseAutoLayout(desktopAutoLayout); ++ setDropIndicatorShown(desktopAutoLayout); ++ if (desktopAutoLayout) { ++ auto sortedUris = layoutItems(); ++ m_proxy_model->setSortedUris(sortedUris); ++ } else { ++ m_proxy_model->setSortedUris(QStringList()); ++ } ++ } ++ }); ++ ++ connect(this, &QAbstractItemView::entered, this, &AdvancedDesktopIconView::onEntered); ++ connect(m_proxy_model, &DesktopItemProxyModel::showHiddenFile, this, &AdvancedDesktopIconView::showHiddenFile); ++} ++ ++AdvancedDesktopIconView::~AdvancedDesktopIconView() ++{ ++ delete m_peonyDbusSer; ++ //saveAllItemPosistionInfos(); ++} ++ ++void AdvancedDesktopIconView::initShoutCut() ++{ ++ QAction *copyAction = new QAction(this); ++ copyAction->setShortcut(QKeySequence::Copy); ++ connect(copyAction, &QAction::triggered, [=]() { ++ if (! GlobalSettings::getInstance()->getValue(ENABLE_SHORTCUT_KEYS).toBool()) { ++ return ; ++ } ++ ++ auto selectedUris = this->getSelections(); ++ if (!selectedUris.isEmpty() && !meetSpecialConditions(selectedUris)){ ++ ClipboardUtils::setClipboardFiles(selectedUris, false); ++ this->viewport()->update(); ++ } ++ }); ++ addAction(copyAction); ++ ++ QAction *cutAction = new QAction(this); ++ cutAction->setShortcut(QKeySequence::Cut); ++ connect(cutAction, &QAction::triggered, [=]() { ++ if (! GlobalSettings::getInstance()->getValue(ENABLE_SHORTCUT_KEYS).toBool()) { ++ return ; ++ } ++ ++ auto selectedUris = this->getSelections(); ++ if (!selectedUris.isEmpty() && !meetSpecialConditions(selectedUris)) ++ { ++ ClipboardUtils::setClipboardFiles(selectedUris, true); ++ //this->update(); ++ this->viewport()->update(); ++ } ++ }); ++ addAction(cutAction); ++ ++ QAction *pasteAction = new QAction(this); ++ pasteAction->setShortcut(QKeySequence::Paste); ++ connect(pasteAction, &QAction::triggered, [=]() { ++ if (! GlobalSettings::getInstance()->getValue(ENABLE_SHORTCUT_KEYS).toBool()) { ++ return ; ++ } ++ ++ if (qApp->clipboard()->mimeData()->hasFormat ("uos/remote-copy")) { ++ auto op = ClipboardUtils::pasteClipboardFiles(this->getDirectoryUri()); ++ if (!op) { ++ viewport()->update(); ++ } ++ } else { ++ //auto clipUris = ClipboardUtils::getClipboardFilesUris(); ++ if (ClipboardUtils::getInstance()->isClipboardHasFiles() && !meetSpecialConditions(this->getSelections())) { ++ auto op = ClipboardUtils::pasteClipboardFiles(this->getDirectoryUri()); ++ if (!op) { ++ viewport()->update(); ++ } ++ } ++ } ++ }); ++ addAction(pasteAction); ++ ++ //add CTRL+D for delete operation ++ auto trashAction = new QAction(this); ++ trashAction->setShortcuts(QList<QKeySequence>()<<Qt::Key_Delete<<QKeySequence(Qt::CTRL + Qt::Key_D)); ++ connect(trashAction, &QAction::triggered, [=]() { ++ if (! GlobalSettings::getInstance()->getValue(ENABLE_SHORTCUT_KEYS).toBool()) { ++ return ; ++ } ++ ++ auto selectedUris = getSelections(); ++ if (!selectedUris.isEmpty() && !meetSpecialConditions(selectedUris)){ ++ FileOperationUtils::trash(selectedUris, true); ++ } ++ }); ++ addAction(trashAction); ++ ++ QAction *undoAction = new QAction(this); ++ undoAction->setShortcut(QKeySequence::Undo); ++ connect(undoAction, &QAction::triggered, ++ [=]() { ++ // do not relayout item with undo. ++ setRenaming(true); ++ FileOperationManager::getInstance()->undo(); ++ }); ++ addAction(undoAction); ++ ++ QAction *redoAction = new QAction(this); ++ redoAction->setShortcut(QKeySequence::Redo); ++ connect(redoAction, &QAction::triggered, ++ [=]() { ++ // do not relayout item with redo. ++ setRenaming(true); ++ FileOperationManager::getInstance()->redo(); ++ }); ++ addAction(redoAction); ++ ++ QAction *zoomInAction = new QAction(this); ++ zoomInAction->setShortcut(QKeySequence::ZoomIn); ++ connect(zoomInAction, &QAction::triggered, [=]() { ++ this->zoomIn(); ++ }); ++ addAction(zoomInAction); ++ ++ QAction *zoomOutAction = new QAction(this); ++ zoomOutAction->setShortcut(QKeySequence::ZoomOut); ++ connect(zoomOutAction, &QAction::triggered, [=]() { ++ this->zoomOut(); ++ }); ++ addAction(zoomOutAction); ++ ++ QAction *renameAction = new QAction(this); ++ renameAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_E)); ++ connect(renameAction, &QAction::triggered, [=]() { ++ auto selections = this->getSelections(); ++ if (selections.count() == 1 && !meetSpecialConditions(selections)) { ++ this->editUri(selections.first()); ++ } ++ }); ++ addAction(renameAction); ++ ++ QAction *removeAction = new QAction(this); ++ removeAction->setShortcut(QKeySequence(Qt::SHIFT + Qt::Key_Delete)); ++ connect(removeAction, &QAction::triggered, [=]() { ++ if (! GlobalSettings::getInstance()->getValue(ENABLE_SHORTCUT_KEYS).toBool()) { ++ return ; ++ } ++ ++ auto selectedUris = this->getSelections(); ++ if (!meetSpecialConditions(selectedUris)){ ++ qDebug() << "delete" << selectedUris; ++ FileOperationUtils::executeRemoveActionWithDialog(selectedUris); ++ } ++ }); ++ addAction(removeAction); ++ ++ QAction *helpAction = new QAction(this); ++ helpAction->setShortcut(Qt::Key_F1); ++ connect(helpAction, &QAction::triggered, this, [=]() { ++ PeonyDesktopApplication::showGuide(); ++ }); ++ addAction(helpAction); ++ ++ auto propertiesWindowAction = new QAction(this); ++ propertiesWindowAction->setShortcuts(QList<QKeySequence>()<<QKeySequence(Qt::ALT + Qt::Key_Return) ++ <<QKeySequence(Qt::ALT + Qt::Key_Enter)); ++ connect(propertiesWindowAction, &QAction::triggered, this, [=]() { ++ DesktopMenu menu(this); ++ if (this->getSelections().count() > 0) ++ { ++ menu.showProperties(this->getSelections()); ++ } ++ else ++ { ++ QString desktopPath = "file://" + QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); ++ menu.showProperties(desktopPath); ++ } ++ }); ++ addAction(propertiesWindowAction); ++ ++ auto newFolderAction = new QAction(this); ++ newFolderAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_N)); ++ connect(newFolderAction, &QAction::triggered, this, [=]() { ++ CreateTemplateOperation op(this->getDirectoryUri(), CreateTemplateOperation::EmptyFolder, tr("New Folder")); ++ op.run(); ++ auto targetUri = op.target(); ++ ++ QTimer::singleShot(300, this, [=]() { ++ this->scrollToSelection(targetUri); ++ ++ for (auto index : m_proxy_model->getAllFileIndexes()) { ++ closePersistentEditor(index); ++ setIndexWidget(index, nullptr); ++ } ++ selectionModel()->clearSelection(); ++ ++ setState(QListView::NoState); ++ auto origin = FileUtils::getOriginalUri(targetUri); ++ auto index = m_proxy_model->mapFromSource(m_model->indexFromUri(origin)); ++ edit(index); ++ }); ++ }); ++ addAction(newFolderAction); ++ ++ QAction *refreshWinAction = new QAction(this); ++ refreshWinAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R)); ++ connect(refreshWinAction, &QAction::triggered, [=]() { ++ this->refresh(); ++ }); ++ addAction(refreshWinAction); ++ ++ QAction *reverseSelectAction = new QAction(this); ++ reverseSelectAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_L)); ++ connect(reverseSelectAction, &QAction::triggered, [=]() { ++ this->invertSelections(); ++ }); ++ addAction(reverseSelectAction); ++ ++ QAction *normalIconAction = new QAction(this); ++ normalIconAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_0)); ++ connect(normalIconAction, &QAction::triggered, [=]() { ++ if (this->zoomLevel() == AdvancedDesktopIconView::Normal) ++ return; ++ this->setDefaultZoomLevel(AdvancedDesktopIconView::Normal); ++ }); ++ addAction(normalIconAction); ++ ++ auto refreshAction = new QAction(this); ++ refreshAction->setShortcut(Qt::Key_F5); ++ connect(refreshAction, &QAction::triggered, this, [=]() { ++ this->refresh(); ++ }); ++ addAction(refreshAction); ++ ++ QAction *editAction = new QAction(this); ++ editAction->setShortcuts(QList<QKeySequence>()<<QKeySequence(Qt::ALT + Qt::Key_E)<<Qt::Key_F2); ++ connect(editAction, &QAction::triggered, this, [=]() { ++ auto selections = this->getSelections(); ++ if (selections.count() == 1) { ++ this->editUri(selections.first()); ++ } else if (selections.count() > 1) { ++ this->editUris(selections); ++ } ++ }); ++ addAction(editAction); ++ ++ auto settings = GlobalSettings::getInstance(); ++ m_show_hidden = settings->isExist(SHOW_HIDDEN_PREFERENCE)? settings->getValue(SHOW_HIDDEN_PREFERENCE).toBool(): false; ++ //show hidden action ++ QAction *showHiddenAction = new QAction(this); ++ showHiddenAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_H)); ++ addAction(showHiddenAction); ++ connect(showHiddenAction, &QAction::triggered, this, [=]() { ++ this->setShowHidden(); ++ }); ++ ++ auto cancelAction = new QAction(this); ++ cancelAction->setShortcut(Qt::Key_Escape); ++ connect(cancelAction, &QAction::triggered, [=]() { ++ if (Peony::ClipboardUtils::isClipboardHasFiles()) ++ { ++ Peony::ClipboardUtils::clearClipboard(); ++ this->update(); ++ } ++ }); ++ addAction(cancelAction); ++ ++ auto *selectAllAction = new QAction(this); ++ selectAllAction->setShortcut(QKeySequence::SelectAll); ++ connect(selectAllAction, &QAction::triggered, this, [=]() { ++ if (! GlobalSettings::getInstance()->getValue(ENABLE_SHORTCUT_KEYS).toBool()) { ++ return ; ++ } ++ ++ this->selectAll(); ++ }); ++ addAction(selectAllAction); ++} ++ ++void AdvancedDesktopIconView::openFileByUri(QString uri) ++{ ++ auto info = FileInfo::fromUri(uri); ++ auto job = new FileInfoJob(info); ++ job->setAutoDelete(); ++ job->connect(job, &FileInfoJob::queryAsyncFinished, [=]() { ++ if ((info->isDir() || info->isVolume() || info->isVirtual())) { ++ QDir dir(info->filePath()); ++ if (! dir.exists()) ++ { ++ Peony::AudioPlayManager::getInstance()->playWarningAudio(); ++ auto result = QMessageBox::question(nullptr, tr("Open Link failed"), ++ tr("File not exist, do you want to delete the link file?"), ++ QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); ++ if (result == QMessageBox::Yes) { ++ qDebug() << "Delete unused symbollink in desktop."; ++ QStringList selections; ++ selections.push_back(uri); ++ FileOperationUtils::trash(selections, true); ++ } ++ return; ++ } ++ ++ if (! info->uri().startsWith("trash://") ++ && ! info->uri().startsWith("computer://") ++ && ! info->canExecute()) ++ { ++ Peony::AudioPlayManager::getInstance()->playWarningAudio(); ++ QMessageBox::critical(nullptr, tr("Open failed"), ++ tr("Open directory failed, you have no permission!")); ++ return; ++ } ++ ++#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) ++ QProcess p; ++ QUrl url = uri; ++ p.setProgram("peony"); ++ p.setArguments(QStringList() << url.toEncoded() <<"%U&"); ++ qint64 pid; ++ p.startDetached(&pid); ++ ++ // send startinfo to kwindowsystem ++ quint32 timeStamp = QX11Info::isPlatformX11() ? QX11Info::appUserTime() : 0; ++ KStartupInfoId startInfoId; ++ startInfoId.initId(KStartupInfo::createNewStartupIdForTimestamp(timeStamp)); ++ startInfoId.setupStartupEnv(); ++ KStartupInfoData data; ++ data.setHostname(); ++ data.addPid(pid); ++ QRect rect = info.get()->property("iconGeometry").toRect(); ++#ifdef KSTARTUPINFO_HAS_SET_ICON_GEOMETRY ++ if (rect.isValid()) ++ data.setIconGeometry(rect); ++#endif ++ data.setLaunchedBy(getpid()); ++ KStartupInfo::sendStartup(startInfoId, data); ++#else ++ QProcess p; ++ QString strq; ++ for (int i = 0;i < uri.length();++i) { ++ if(uri[i] == ' '){ ++ strq += "%20"; ++ }else{ ++ strq += uri[i]; ++ } ++ } ++ ++ p.startDetached("/usr/bin/peony", QStringList()<<strq<<"%U&"); ++#endif ++ } else { ++ if (!(info->isDesktopFile() && execSharedFileLink(uri))) { ++ FileLaunchManager::openAsync(uri, false, false); ++ } ++ } ++ this->clearSelection(); ++ }); ++ job->queryAsync(); ++} ++ ++void AdvancedDesktopIconView::initDoubleClick() ++{ ++ connect(this, &QAbstractItemView::activated, this, [=](const QModelIndex &index) { ++ qDebug() << "double click" << index.data(FileItemModel::UriRole); ++ auto uri = index.data(FileItemModel::UriRole).toString(); ++ openFileByUri(uri); ++ }, Qt::UniqueConnection); ++} ++ ++void AdvancedDesktopIconView::setMargins() ++{ ++ int settingsislandposition = m_panelSetting->get("settingsislandposition").toInt(); ++ int paneltype = m_panelSetting->get("paneltype").toInt(); ++ int position = m_panelSetting->get("panelposition").toInt(); ++ int margins = m_panelSetting->get("panelsize").toInt(); ++ ++ if (settingsislandposition == 1 && paneltype == 1) { ++ setViewportMargins(0, 32, 0, margins); ++ return; ++ } ++ switch (position) { ++ case 1: { ++ setViewportMargins(0, margins, 0, 0); ++ break; ++ } ++ case 2: { ++ setViewportMargins(margins, 0, 0, 0); ++ break; ++ } ++ case 3: { ++ setViewportMargins(0, 0, margins, 0); ++ break; ++ } ++ default: { ++ setViewportMargins(0, 0, 0, margins); ++ break; ++ } ++ } ++} ++ ++void AdvancedDesktopIconView::setId(int id) ++{ ++ m_proxy_model->setId(id); ++ m_id = id ; ++} ++ ++QRect AdvancedDesktopIconView::visualRect(const QModelIndex &index) const ++{ ++ if (!index.isValid()) ++ return QRect(); ++ ++ QRect rect = QRect(QPoint(0, 0), m_gridSize); ++ rect.adjust(5, 5, -5, -5); ++ // todo: 通过model data获取index位置 ++ bool ok = false; ++ auto pos = getIndexGridPos(index, &ok); ++ if (ok) { ++ //qDebug()<<index.data()<<pos; ++ rect.moveTo(pos.x() * m_gridSize.width() + m_margin.x(), pos.y() * m_gridSize.height() + m_margin.y()); ++ if (layoutDirection() == Qt::RightToLeft) { ++ int vewportX = viewport()->rect().topRight().x(); ++ int x = vewportX - rect.topRight().x() ; ++ rect = QRect(x, rect.y(), rect.width(), rect.height()); ++ } ++ } else { ++ qWarning()<<"no pos for item"<<index.data()<<pos; ++ } ++ ++ return rect; ++} ++ ++void AdvancedDesktopIconView::scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint hint) ++{ ++ Q_UNUSED(index) ++ Q_UNUSED(hint) ++ return; ++} ++ ++QModelIndex AdvancedDesktopIconView::indexAt(const QPoint &point) const ++{ ++ // todo: 通过model data获取index位置,或者通过缓存获取 ++ for (int i = 0; i < model()->rowCount(); i++) { ++ auto index = model()->index(i, 0); ++ auto tmpRect = visualRect(index); ++ if (tmpRect.contains(point)) ++ return index; ++ } ++ return QModelIndex(); ++} ++ ++QModelIndex AdvancedDesktopIconView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers) ++{ ++ // todo: 实现键盘切换选择 ++ return QModelIndex(); ++} ++ ++int AdvancedDesktopIconView::horizontalOffset() const ++{ ++ return 0; ++} ++ ++int AdvancedDesktopIconView::verticalOffset() const ++{ ++ return 0; ++} ++ ++bool AdvancedDesktopIconView::isIndexHidden(const QModelIndex &index) const ++{ ++ // todo: 根据屏幕id匹配 ++ bool isHidden = /*m_id == index.data(ScreenIdRole).toInt() ? true :*/ false; ++ return isHidden; ++} ++ ++// fixme: 非qlistview不能在index非法的情况下触发选择,需要在mouseEvent中处理此流程 ++void AdvancedDesktopIconView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command) ++{ ++ qDebug()<<"set selection"<<rect<<command; ++ auto tmpRect = rect.normalized(); ++ QItemSelection selection; ++ if (tmpRect.isEmpty()) ++ return; ++ ++ bool notMultiSelection = (rect.width() == 1 && rect.height() == 1) || state() == DragSelectingState ; ++ if (m_shift_key_pressed && !notMultiSelection) { ++ //shift 按键连选,获取首尾相对坐标,中间的所有项都选中 ++ QPoint first = rect.topLeft(); ++ QPoint last = rect.bottomRight(); ++ if (first.x() > last.x()) ++ qSwap(first, last); ++ QPoint leftPos ,rightPos; ++ auto leftIndex = indexAt(first); ++ bool leftOk = false; ++ leftPos = getIndexGridPos(leftIndex, &leftOk); ++ auto rightIndex= indexAt(last); ++ bool rightOk = false; ++ rightPos = getIndexGridPos(rightIndex, &rightOk); ++ ++ if (leftPos.x() == rightPos.x() && leftPos.y() > rightPos.y()) ++ qSwap(leftPos, rightPos); ++ ++ if (leftOk && rightOk) { ++ for (int i = 0; i < model()->rowCount(); i++) { ++ auto index = model()->index(i, 0); ++ bool ok = false; ++ auto pos = getIndexGridPos(index, &ok); ++ ++ if (gridPosLesserThan(pos, leftPos)|| gridPosLesserThan(rightPos, pos)) { ++ continue; ++ } ++ selection.select(index, index); ++ // 单击重叠的图标只选中一个 ++ if (command == QItemSelectionModel::ClearAndSelect || command == QItemSelectionModel::Select) ++ break; ++ } ++ ++ selectionModel()->select(selection, command); ++ return; ++ } ++ } ++ ++ // todo 性能优化 ++ for (int i = 0; i < model()->rowCount(); i++) { ++ auto index = model()->index(i, 0); ++ auto indexRect = visualRect(index); ++ if (!indexRect.intersects(tmpRect) || !index.isValid()) ++ continue; ++ selection.select(index, index); ++ // 单击重叠的图标只选中一个 ++ if (command == QItemSelectionModel::ClearAndSelect || command == QItemSelectionModel::Select) ++ break; ++ } ++ ++ selectionModel()->select(selection, command); ++} ++ ++QRegion AdvancedDesktopIconView::visualRegionForSelection(const QItemSelection &selection) const ++{ ++ QRegion region; ++ auto indexes = selection.indexes(); ++ for (auto index : indexes) { ++ if (!index.isValid()) ++ continue; ++ auto tmpRect = visualRect(index); ++ //防止分数缩放和显示覆盖影响 ++ region += tmpRect.adjusted(-5, -5, 5, 5); ++ } ++ region.intersects(this->viewport()->rect()); ++ return region; ++} ++ ++QStyleOptionViewItem AdvancedDesktopIconView::viewOptions() const ++{ ++ auto option = QAbstractItemView::viewOptions(); ++ option.decorationAlignment = Qt::AlignTop|Qt::AlignHCenter; ++ option.decorationPosition = QStyleOptionViewItem::Top; ++ option.displayAlignment = Qt::AlignTop|Qt::AlignHCenter; ++ option.textElideMode = Qt::ElideMiddle; ++ option.features = QStyleOptionViewItem::HasDisplay|QStyleOptionViewItem::HasDecoration|QStyleOptionViewItem::WrapText; ++ return option; ++} ++ ++void AdvancedDesktopIconView::startDrag(Qt::DropActions supportedActions) ++{ ++ // fixme默认为moveaction,支持copyaction ++ m_rubberBand->setVisible(false); ++ ++ auto indexes = selectedIndexes(); ++ if (indexes.count() > 0) { ++ auto pos = m_dragPressPos; ++ qreal scale = 1.0; ++ QWidget *window = this->window(); ++ if (window) { ++ auto windowHandle = window->windowHandle(); ++ if (windowHandle) { ++ scale = windowHandle->devicePixelRatio(); ++ } ++ } ++ ++ auto drag = new QDrag(this); ++ drag->setMimeData(model()->mimeData(indexes)); ++ ++ QRegion rect; ++ QHash<QModelIndex, QRect> indexRectHash; ++ for (auto index : indexes) { ++ rect += (visualRect(index)); ++ indexRectHash.insert(index, visualRect(index)); ++ } ++ ++ QRect realRect = rect.boundingRect(); ++ ++ // fix #78263, text displayment is not completed. ++ //realRect.adjust(-5, -5, 5, 5); ++ ++ realRect.adjust(-15, -15, 15, 15); ++ QPixmap pixmap(realRect.size() * scale); ++ pixmap.fill(Qt::transparent); ++ pixmap.setDevicePixelRatio(scale); ++ QPainter painter(&pixmap); ++ // try fixing #190315, text shadow displayment issue while compositing not running. ++ bool shouldDrawBackground = !QX11Info::isCompositingManagerRunning(); ++ for (auto index : indexes) { ++ painter.save(); ++ painter.translate(indexRectHash.value(index).topLeft() - rect.boundingRect().topLeft()); ++ if (shouldDrawBackground) { ++ painter.setPen(qApp->palette().highlight().color()); ++ painter.setBrush(qApp->palette().highlight()); ++ painter.drawRoundedRect(QRect(0, 0, m_gridSize.width(), m_gridSize.height()).adjusted(1, 1, -1, -1), 6, 6); ++ } ++ QStyleOptionViewItem opt = viewOptions(); ++ auto viewItemDelegate = qobject_cast<DesktopIconViewDelegate *>(itemDelegate()); ++ viewItemDelegate->initIndexOption(&opt, index); ++ opt.rect.setSize(visualRect(index).size()); ++ itemDelegate()->paint(&painter, opt, index); ++ painter.restore(); ++ } ++ ++ drag->setPixmap(pixmap); ++ drag->setHotSpot(pos - rect.boundingRect().topLeft() - QPoint(viewportMargins().left(), viewportMargins().top())); ++ drag->setDragCursor(QPixmap(), m_ctrl_key_pressed? Qt::CopyAction: Qt::MoveAction); ++ drag->exec(m_ctrl_key_pressed? Qt::CopyAction: Qt::MoveAction); ++ ++ } else { ++ return QAbstractItemView::startDrag(Qt::MoveAction|Qt::CopyAction); ++ } ++ ++} ++ ++void AdvancedDesktopIconView::reset() ++{ ++ // todo 待优化 ++ if (m_availableColumnCount == 1 && m_availableRowCount == 1) ++ return; ++ if (model() && m_itemPosHash.count() < model()->rowCount()) { ++ bool ok = false; ++ qDebug()<<model()->rowCount(); ++ for (int row = 0; row < model()->rowCount(); row++) { ++ // todo: 桌面整理时的表现 ++ auto index = model()->index(row, 0, QModelIndex()); ++ auto pos = getIndexGridPos(index, &ok); ++ if (!ok) { ++ auto emptypos = findNextEmptyGridPos(); ++ if (emptypos.x() < 0) { ++ qWarning()<<"no empty pos"; ++ //如果没有空位则后面的item不排序 ++ break; ++ } ++ pos = emptypos; ++ } ++ auto uri = index.data(UriRole).toString(); ++ model()->setData(index, pos, PositionRole); ++ setFileMetaInfoPos(uri, pos); ++ } ++ } ++ QAbstractItemView::reset(); ++} ++ ++void AdvancedDesktopIconView::rowsInserted(const QModelIndex &parent, int start, int end) ++{ ++// // 如果没有找到排序配置,则跳过此流程,避免出现顺序异常改变,这可能出现在首次升级且异常关闭的场景 ++ bool ok = false; ++ bool isFull = false; ++ QPoint pos = QPoint(0, 0); ++ if (m_proxy_model->getDesktopUseAutoLayout()) { ++ QAbstractItemView::rowsInserted(parent, start, end); ++ for (int i = start; i <= end; i++) { ++ auto index = model()->index(i, 0, parent); ++ auto uriAdded = index.data(Qt::UserRole).toString(); ++ if (!m_autoArrange.contains(uriAdded)) { ++ bool isRenaming = false; ++ if (!isFull) { ++ pos = getIndexGridPos(index, &ok); ++ if (!ok || pos == QPoint(-1,-1) || (!refreshing && !m_itemPosHash.keys(pos).isEmpty())) { ++ isRenaming = true; ++ auto emptypos = findNextEmptyGridPos(); ++ if (emptypos.x() < 0) { ++ qWarning()<<"no empty pos"; ++ //如果没有空位则后面的item不排序 ++ emptypos = QPoint(0, 0); ++ if (!m_storageBox.contains(uriAdded)) ++ m_storageBox.append(uriAdded); ++ isFull = true; ++ } ++ pos = emptypos; ++ } ++ } ++ model()->setData(index, pos, PositionRole); ++ setFileMetaInfoPos(uriAdded, pos); ++ if (!refreshing) { ++ if (!isRenaming) { ++ int index = pos.x() * m_availableRowCount + pos.y(); ++ m_autoArrange.insert(index, uriAdded); ++ } else { ++ //刷新的时候不是按照顺序加载,统一在刷新之后获取列表 ++ m_autoArrange.append(uriAdded); ++ } ++ } ++ } ++ } ++ return; ++ } ++ ++ for (int row = start; row <= end; row++) { ++ // todo: 桌面整理时的表现 ++ auto index = model()->index(row, 0, parent); ++ QString uri = index.data(UriRole).toString(); ++ m_itemPosHash.remove(uri); ++ ++ if (!isFull) { ++ pos = getIndexGridPos(index, &ok); ++ ++ if (!ok || pos == QPoint(-1,-1) || (!refreshing && !m_itemPosHash.keys(pos).isEmpty())) { ++ auto emptypos = findNextEmptyGridPos(); ++ if (emptypos.x() < 0) { ++ qWarning()<<"no empty pos"; ++ //如果没有空位则后面的item不排序 ++ emptypos = QPoint(0, 0); ++ if (!m_storageBox.contains(uri)) ++ m_storageBox.append(uri); ++ isFull = true; ++ } ++ pos = emptypos; ++ } ++ } ++ qDebug() << "-----------" <<m_itemPosHash; ++ model()->setData(index, pos, PositionRole); ++ setFileMetaInfoPos(uri, pos); ++ } ++ ++ QAbstractItemView::rowsInserted(parent, start, end); ++} ++ ++void AdvancedDesktopIconView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) ++{ ++ for (int row = start; row <= end; row++) { ++ auto index = model()->index(row, 0); ++ auto uri = index.data(Qt::UserRole).toString(); ++ m_itemPosHash.remove(uri); ++ m_resolutionItemPosHash.remove(uri); ++ m_autoArrange.removeOne(uri); ++ m_storageBox.removeOne(uri); ++ qDebug() <<"AdvancedDesktopIconView::rowsAboutToBeRemoved" <<uri; ++ } ++ ++ QAbstractItemView::rowsAboutToBeRemoved(parent, start, end); ++ clearAllIndexWidgets(); ++} ++ ++void AdvancedDesktopIconView::paintEvent(QPaintEvent *e) ++{ ++ QPainter painter(this->viewport()); ++ //QRect renderRect = e->rect(); ++ QRect renderRect = viewport()->rect(); ++ // todo: caculate item to be rendered. ++ int num = model()->rowCount(); ++ for (int i = 0; i < model()->rowCount(); i++) { ++ auto options = viewOptions(); ++ auto index = model()->index(i, 0); ++ if (selectionModel()->selectedIndexes().contains(index)) ++ options.state |= QStyle::State_Selected; ++ auto tmpRect = visualRect(index); ++ ++ options.state.setFlag(QStyle::State_MouseOver, m_hoverIndex == index); ++ options.rect = tmpRect; ++ auto delegate = qobject_cast<QStyledItemDelegate*>(itemDelegate()); ++ delegate->paint(&painter, options, index); ++ } ++ if ((state() == DraggingState || m_isDragging) && m_proxy_model->getDesktopUseAutoLayout()) { ++ QPainter p(viewport()); ++ QStyleOption opt; ++ opt.init(this); ++ opt.rect = m_dropIndicatorRect; ++ opt.palette.setColor(QPalette::Highlight, opt.palette.highlightedText().color()); ++ style()->drawPrimitive(QStyle::PE_IndicatorItemViewItemDrop, &opt, &p, this); ++ } ++} ++ ++void AdvancedDesktopIconView::refresh() ++{ ++ this->setCursor(QCursor(Qt::WaitCursor)); ++ //fix refresh clear copy files issue, link to bug#109247 ++ if (Peony::ClipboardUtils::isDesktopFilesBeCut()) ++ Peony::ClipboardUtils::clearClipboard();/* Refresh clear cut status */ ++ if (!m_model) ++ return; ++ ++ if (refreshing) ++ return; ++ refreshing = true; ++ m_itemPosHash.clear(); ++ m_autoArrange.clear(); ++ m_model->refresh(); ++} ++ ++void AdvancedDesktopIconView::resizeEvent(QResizeEvent *e) ++{ ++ QAbstractItemView::resizeEvent(e); ++ // todo: 批处理 ++ recalculateAvailableRowAndColumnCount(); ++ // todo: 通过model data获取index位置,或者通过缓存获取 ++ this->viewport()->update(); ++} ++ ++void AdvancedDesktopIconView::dropEvent(QDropEvent *event) ++{ ++ m_isDragging = false; ++ m_rubberBand->setVisible(false); ++ // fix #122768, dirty region issues. ++ this->viewport()->update(); ++ m_real_do_edit = false; ++ //qDebug()<<"drop event"; ++ /*! ++ \todo ++ fix the bug that move drop action can not move the desktop ++ item to correct position. ++ ++ i use copy action to avoid this bug, but the drop indicator ++ is incorrect. ++ */ ++ m_edit_trigger_timer.stop(); ++ if (event->keyboardModifiers() & Qt::ControlModifier) { ++ m_ctrl_key_pressed = true; ++ } else { ++ m_ctrl_key_pressed = false; ++ } ++ ++ auto action = m_ctrl_key_pressed ? Qt::CopyAction : Qt::MoveAction; ++ if (event->keyboardModifiers() & Qt::ShiftModifier) { ++ action = Qt::TargetMoveAction; ++ } ++ qDebug() << "DesktopIconView dropEvent" <<action; ++ auto view = qobject_cast<AdvancedDesktopIconView *>(event->source()); ++ if (m_proxy_model->getDesktopUseAutoLayout() && m_dropIndicatorPos != DropIndicatorPosition::OnItem && !m_ctrl_key_pressed) { ++ qDebug()<<"drop do auto layout move"; ++ bool sucess = dropWhenAotoArrange(event); ++ if (sucess) { ++ m_pressedPos = QPoint(-1,-1); ++ view->m_pressedPos = QPoint(-1,-1); ++ return; ++ } ++ } ++ ++ auto pos = event->pos(); ++ auto index = indexAt(pos); ++ if (index.isValid() || m_ctrl_key_pressed) ++ { ++ qDebug() <<"DesktopIconView index copyAction:"; ++ auto urls = event->mimeData()->urls(); ++ QString homePath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); ++ QStringList uris; ++ for (auto url : urls) ++ { ++ if (url.toString() == "computer:///") ++ uris << "computer:///"; ++ else ++ uris << url.path(); ++ } ++ ++ //fix can drag copy home folder issue, link to bug#64824 ++ if (uris.contains(homePath) || uris.contains("computer:///")) ++ return; ++ } ++ ++ if (!m_ctrl_key_pressed) { ++ bool bmoved = false; ++ if (index.isValid()) { ++ auto uri = m_proxy_model->mapToSource(index).data(Qt::UserRole).toString(); ++ auto info = FileInfo::fromUri(uri); ++ if (!info->isDir()||event->mimeData()->urls().contains(uri)) { ++ m_pressedPos = QPoint(-1,-1); ++ return; ++ } ++ bmoved = true; ++ } ++ bool sucess = true; ++ if (bmoved) { ++ //move file to desktop folder ++ qDebug() << "DesktopIconView move file to folder"; ++ for (auto uuri : event->mimeData()->urls()) { ++ if ("trash:///" == uuri.toDisplayString() || "computer:///" == uuri.toDisplayString()) { ++ m_pressedPos = QPoint(-1,-1); ++ return; ++ } ++ } ++ ++ sucess = m_model->dropMimeData(event->mimeData(), action, -1, -1, this->indexAt(event->pos())); ++ } /*else {*/ ++ // do not trigger file operation, link to: #66345 ++// m_model->setAcceptDropAction(false); ++// QAbstractItemView::dropEvent(event); ++// m_model->setAcceptDropAction(true); ++// } ++ if (!sucess) { ++ m_pressedPos = QPoint(-1,-1); ++ return; ++ } ++ ++ if (m_proxy_model->getDesktopUseAutoLayout()) { ++ bool bDropToOtherScreen = false; ++ auto selectedItems = selectedIndexes(); ++ if (view) { ++ selectedItems = selectedIndexes(); ++ if (this != event->source()) { ++ bDropToOtherScreen = true; ++ selectedItems = view->selectedIndexes(); ++ } ++ auto destSortedUris = m_autoArrange; ++ auto sourceSortedUris = view->m_autoArrange; ++ for (auto index : selectedItems) { ++ auto uri = index.data(Qt::UserRole).toString(); ++ if (bDropToOtherScreen) { ++ sourceSortedUris.removeOne(uri); ++ } else { ++ destSortedUris.removeOne(uri); ++ } ++ } ++ relayoutExsitingItemsAndUpdate(destSortedUris); ++ if (bDropToOtherScreen) { ++ view->relayoutExsitingItemsAndUpdate(sourceSortedUris); ++ } ++ m_pressedPos = QPoint(-1,-1); ++ view->m_pressedPos = QPoint(-1,-1); ++ return; ++ } ++ } ++ } ++ if (event->source() == this && !m_ctrl_key_pressed) { ++ qDebug() <<"DesktopIconView index:" <<index <<index.isValid(); ++ // 计算偏移量,并且设置item data ++ //QModelIndex pressedIndex = indexAt(m_pressedPos); ++ QPoint pressedGridPos = getGridPosFromMousePos(m_pressedPos); ++ QPoint dropedGridPos = getGridPosFromMousePos(pos); ++ ++ if (pressedGridPos == dropedGridPos) { ++ event->ignore(); ++ m_pressedPos = QPoint(-1,-1); ++ return; ++ } ++ ++ auto gridOffset = dropedGridPos - pressedGridPos; ++ qDebug()<<gridOffset; ++ ++ QHash<QString, QModelIndex> tmpIndexes; ++ QHash<QString, QModelIndex> relayoutIndexes; ++ for (QModelIndex index : selectedIndexes()) { ++ QString uri = index.data(UriRole).toString(); ++ tmpIndexes.insert(uri, index); ++ bool ok = false; ++ auto pos = getIndexGridPos(index, &ok); ++ if (ok && !isInvalidPoint(pos)) { ++ pos += gridOffset; ++ if (isInvalidPoint(pos)|| !m_itemPosHash.keys(pos).isEmpty()) { ++ relayoutIndexes.insert(uri, index); ++ continue; ++ } ++ } else { ++ relayoutIndexes.insert(uri, index); ++ continue; ++ } ++ ++ qDebug()<<" ----------- "<<m_itemPosHash; ++ model()->setData(index, pos, PositionRole); ++ setFileMetaInfoPos(uri, pos); ++ } ++ ++ for (QString uri : relayoutIndexes.keys()) { ++ QPoint pos = findNextEmptyGridPos(dropedGridPos); ++ if (!isInvalidPoint(pos)) { ++ qDebug()<<" ----------- "<<m_itemPosHash; ++ auto index = relayoutIndexes.value(uri); ++ model()->setData(index, pos, PositionRole); ++ setFileMetaInfoPos(uri, pos); ++ } ++ } ++ ++ event->ignore(); ++ m_pressedPos = QPoint(-1,-1); ++ return; ++ } else if (!m_ctrl_key_pressed && dragToOtherScreen(event)) { ++ event->ignore(); ++ m_pressedPos = QPoint(-1,-1); ++ return; ++ } else if (!index.isValid() && (event->proposedAction() == Qt::CopyAction || event->proposedAction() & Qt::MoveAction && m_ctrl_key_pressed)) { ++ // fixme: 拷贝 ++ m_model->dropMimeData(event->mimeData(), action, -1, -1, this->indexAt(event->pos())); ++ m_pressedPos = QPoint(-1,-1); ++ return; ++ } ++ ++ return QAbstractItemView::dropEvent(event); ++} ++ ++bool AdvancedDesktopIconView::dropWhenAotoArrange(QDropEvent *event) ++{ ++ qDebug() << "[AdvancedDesktopIconView::dropWhenAotoArrange]" <<m_itemPosHash << m_autoArrange; ++ ++ bool bDropToOtherScreen = false; ++ auto selectedItems = selectedIndexes(); ++ auto view = qobject_cast<AdvancedDesktopIconView*>(event->source()); ++ if (!view) { ++ return false; ++ } ++ if (this != event->source() && view) { ++ bDropToOtherScreen = true; ++ selectedItems = view->selectedIndexes(); ++ } ++ if (m_dropIndicatorRect.isValid()) { ++ auto currentHoverIndex = indexAt(m_dropIndicatorRect.center() + (m_dropIndicatorPos == DropIndicatorPosition::BelowItem? QPoint(0, -m_gridSize.height()/2): QPoint())); ++ if (currentHoverIndex.isValid()) { ++ auto currentHoverUri = currentHoverIndex.data(Qt::UserRole).toString(); ++ auto destSortedUris = m_autoArrange; ++ auto sourceSortedUris = view->m_autoArrange; ++ QStringList draggedUris; ++ for (int i = 0; i < destSortedUris.count(); i++) { ++ draggedUris.append(QString()); ++ } ++ int insertedPos = destSortedUris.indexOf(currentHoverUri); ++ if (m_dropIndicatorPos == DropIndicatorPosition::BelowItem) { ++ insertedPos++; ++ } ++ ++ if (bDropToOtherScreen) { ++ for (auto index : selectedItems) { ++ auto uri = index.data(Qt::UserRole).toString(); ++ sourceSortedUris.removeOne(uri); ++ view->model()->setData(index, m_id, ScreenIdRole); ++ destSortedUris.insert(insertedPos, uri); ++ } ++ } else { ++ for (auto index : selectedItems) { ++ auto uri = index.data(Qt::UserRole).toString(); ++ int pos = destSortedUris.indexOf(uri); ++ destSortedUris.replace(pos, QString()); ++ draggedUris.replace(pos, uri); ++ } ++ ++ draggedUris.removeAll(QString()); ++ ++ //draggedUris.reserve(draggedUris.count()); ++ for (int i = draggedUris.count() - 1; i >= 0; i--) { ++ auto uri = draggedUris[i]; ++ destSortedUris.insert(insertedPos, uri); ++ } ++ destSortedUris.removeAll(QString()); ++ } ++ ++ relayoutExsitingItemsAndUpdate(destSortedUris); ++ if (bDropToOtherScreen) { ++ view->relayoutExsitingItemsAndUpdate(sourceSortedUris); ++ } ++ m_proxy_model->setSortedUris(destSortedUris); ++ return true; ++ } else { ++ qCritical()<<"has drop indicator rect but could not find current hovered index"; ++ } ++ } else { ++ bool dropToEnd = true; ++ auto posIndicator = event->pos(); ++ for (auto pos : m_itemPosHash.values()) { ++ QRect rect(QPoint(pos.x() * m_gridSize.width(), pos.y() * m_gridSize.height()), m_gridSize); ++ if (gridPosLesserThan(posIndicator, rect.center())) { ++ dropToEnd = false; ++ break; ++ } ++ } ++ if (dropToEnd) { ++ auto sortedUris = m_autoArrange; ++ QStringList draggedUris; ++ for (int i = 0; i < sortedUris.count(); i++) { ++ draggedUris.append(QString()); ++ } ++ if (!bDropToOtherScreen) { ++ for (auto index : selectedItems) { ++ auto uri = index.data(Qt::UserRole).toString(); ++ int pos = sortedUris.indexOf(uri); ++ sortedUris.replace(pos, QString()); ++ draggedUris.replace(pos, uri); ++ } ++ sortedUris.removeAll(QString()); ++ draggedUris.removeAll(QString()); ++ sortedUris.append(draggedUris); ++ } else { ++ auto destSortedUris = view->m_autoArrange; ++ for (auto index : selectedItems) { ++ auto uri = index.data(Qt::UserRole).toString(); ++ destSortedUris.removeOne(uri); ++ draggedUris.append(uri); ++ view->model()->setData(index, m_id, ScreenIdRole); ++ } ++ sortedUris.removeAll(QString()); ++ draggedUris.removeAll(QString()); ++ sortedUris.append(draggedUris); ++ view->relayoutExsitingItemsAndUpdate(destSortedUris); ++ } ++ ++ relayoutExsitingItemsAndUpdate(sortedUris); ++ return true; ++ } else { ++ qDebug()<<"do nothing for drop auto layout"; ++ if (m_dropIndicatorPos != OnItem) { ++ // 如果不是移动拖拽到图标上,则不触发后续拖拽流程,避免出现图标未自动排列的情况 ++ return true; ++ } ++ } ++ } ++ return false; ++} ++ ++void AdvancedDesktopIconView::wheelEvent(QWheelEvent *e) ++{ ++ if (QApplication::keyboardModifiers() == Qt::ControlModifier) ++ { ++ if (e->delta() > 0) { ++ zoomIn(); ++ } else { ++ zoomOut(); ++ } ++ } ++} ++ ++void AdvancedDesktopIconView::keyPressEvent(QKeyEvent *e) ++{ ++ switch (e->key()) { ++ case Qt::Key_Home: { ++ QPoint homePos; ++ QModelIndex homeIndex; ++ for (int i = 0; i < m_proxy_model->rowCount(); i++) { ++ auto index = m_proxy_model->index(i, 0); ++ QPoint postion = index.data(PositionRole).toPoint(); ++ if (!gridPosLesserThan(homePos, postion)) { ++ homeIndex = index; ++ homePos = index.data(PositionRole).toPoint(); ++ } ++ } ++ selectionModel()->select(homeIndex, QItemSelectionModel::SelectCurrent); ++ break; ++ } ++ case Qt::Key_End: { ++ QPoint endPos; ++ QModelIndex endIndex; ++ for (int i = 0; i < m_proxy_model->rowCount(); i++) { ++ auto index = m_proxy_model->index(i, 0); ++ QPoint postion = index.data(PositionRole).toPoint(); ++ if (gridPosLesserThan(endPos, postion)) { ++ endIndex = index; ++ endPos = postion; ++ } ++ } ++ selectionModel()->select(endIndex, QItemSelectionModel::SelectCurrent); ++ break; ++ } ++ case Qt::Key_Up: { ++ if (getSelections().isEmpty()) { ++ selectionModel()->select(model()->index(0, 0), QItemSelectionModel::SelectCurrent); ++ } else { ++ auto index = selectionModel()->selectedIndexes().first(); ++ auto center = visualRect(index).center(); ++ auto up = center - QPoint(0, m_gridSize.height()); ++ auto upIndex = indexAt(up); ++ if (upIndex.isValid()) { ++ clearAllIndexWidgets(); ++ selectionModel()->select(upIndex, QItemSelectionModel::SelectCurrent); ++ auto delegate = qobject_cast<DesktopIconViewDelegate *>(itemDelegate()); ++ auto indexWidget = new DesktopIndexWidget(delegate, viewOptions(), upIndex, this); ++ setIndexWidget(upIndex, indexWidget); ++ indexWidget->move(visualRect(upIndex).topLeft()); ++ } ++ } ++ return; ++ } ++ case Qt::Key_Down: { ++ if (getSelections().isEmpty()) { ++ selectionModel()->select(model()->index(0, 0), QItemSelectionModel::SelectCurrent); ++ } else { ++ auto index = selectionModel()->selectedIndexes().first(); ++ auto center = visualRect(index).center(); ++ auto down = center + QPoint(0, m_gridSize.height()); ++ auto downIndex = indexAt(down); ++ if (downIndex.isValid()) { ++ clearAllIndexWidgets(); ++ selectionModel()->select(downIndex, QItemSelectionModel::SelectCurrent); ++ auto delegate = qobject_cast<DesktopIconViewDelegate *>(itemDelegate()); ++ auto indexWidget = new DesktopIndexWidget(delegate, viewOptions(), downIndex, this); ++ setIndexWidget(downIndex, indexWidget); ++ indexWidget->move(visualRect(downIndex).topLeft()); ++ } ++ } ++ return; ++ } ++ case Qt::Key_Left: { ++ if (getSelections().isEmpty()) { ++ selectionModel()->select(model()->index(0, 0), QItemSelectionModel::SelectCurrent); ++ } else { ++ auto index = selectionModel()->selectedIndexes().first(); ++ auto center = visualRect(index).center(); ++ auto left = center - QPoint(m_gridSize.width(), 0); ++ auto leftIndex = indexAt(left); ++ if (leftIndex.isValid()) { ++ clearAllIndexWidgets(); ++ selectionModel()->select(leftIndex, QItemSelectionModel::SelectCurrent); ++ auto delegate = qobject_cast<DesktopIconViewDelegate *>(itemDelegate()); ++ auto indexWidget = new DesktopIndexWidget(delegate, viewOptions(), leftIndex, this); ++ setIndexWidget(leftIndex, indexWidget); ++ indexWidget->move(visualRect(leftIndex).topLeft()); ++ } ++ } ++ return; ++ } ++ case Qt::Key_Right: { ++ if (getSelections().isEmpty()) { ++ selectionModel()->select(model()->index(0, 0), QItemSelectionModel::SelectCurrent); ++ } else { ++ auto index = selectionModel()->selectedIndexes().first(); ++ auto center = visualRect(index).center(); ++ auto right = center + QPoint(m_gridSize.width(), 0); ++ auto rightIndex = indexAt(right); ++ if (rightIndex.isValid()) { ++ clearAllIndexWidgets(); ++ selectionModel()->select(rightIndex, QItemSelectionModel::SelectCurrent); ++ auto delegate = qobject_cast<DesktopIconViewDelegate *>(itemDelegate()); ++ auto indexWidget = new DesktopIndexWidget(delegate, viewOptions(), rightIndex, this); ++ setIndexWidget(rightIndex, indexWidget); ++ } ++ } ++ return; ++ } ++ case Qt::Key_Shift: ++ m_shift_key_pressed = true; ++ case Qt::Key_Control: ++ m_ctrl_or_shift_pressed = true; ++ break; ++ case Qt::Key_Enter: ++ case Qt::Key_Return: ++ { ++ auto selections = this->getSelections(); ++ for (auto uri : selections) ++ { ++ openFileByUri(uri); ++ } ++ } ++ break; ++ default: ++ return QAbstractItemView::keyPressEvent(e); ++ } ++} ++ ++void AdvancedDesktopIconView::keyReleaseEvent(QKeyEvent *e) ++{ ++ QAbstractItemView::keyReleaseEvent(e); ++ m_ctrl_or_shift_pressed = false; ++ m_ctrl_key_pressed = false; ++ m_shift_key_pressed = false; ++} ++ ++void AdvancedDesktopIconView::recalculateAvailableRowAndColumnCount() ++{ ++ m_gridSize = QSize(10, 10); // padding: 5 ++ m_gridSize += iconSize(); // icon size ++ m_gridSize += QSize(0, 5); // text-icon-padding: 5 ++ m_gridSize += QSize(0, qApp->fontMetrics().height()*2); // 2 line text height, todo: fix bo_CN ++ m_gridSize.setWidth(qMax(qApp->fontMetrics().averageCharWidth()*5 + 10, m_gridSize.width() + 31)); ++ ++ auto size = viewport()->rect().size(); ++ m_availableRowCount = qMax(1, size.height()/m_gridSize.height()); ++ m_availableColumnCount = qMax(1, size.width()/m_gridSize.width()); ++ ++ reset(); ++ ++ resolutionChange(); ++ ++ checkItemsOver(); ++ ++ viewport()->update(); //update all item ++} ++ ++QPoint AdvancedDesktopIconView::getIndexGridPos(const QModelIndex &index, bool *ok) const ++{ ++ //fix bug#249915, unzip file to desktop crash issue ++ if (! index.isValid() || ! index.data().isValid()) { ++ return invalidPoint(); ++ } ++ ++ *ok = index.data(PositionRole).isValid(); ++ if (!(*ok)) ++ return invalidPoint(); ++ return index.data(PositionRole).toPoint(); ++} ++ ++QPoint AdvancedDesktopIconView::findNextEmptyGridPos(const QPoint &startGridPos) ++{ ++ for (int column = 0; column < m_availableColumnCount; column++) { ++ for (int row = 0; row < m_availableRowCount; row++) { ++ QPoint pos = QPoint(column, row); ++ if (gridPosLesserThan(pos, startGridPos)) ++ continue; ++ if (m_itemPosHash.keys(pos).isEmpty()) ++ return pos; ++ } ++ } ++ return invalidPoint(); ++} ++ ++bool AdvancedDesktopIconView::isInvalidPoint(const QPoint &point) ++{ ++ return point.x() < 0 || point.x() >= m_availableColumnCount || point.y() < 0 || point.y() >= m_availableRowCount; ++} ++ ++QPoint AdvancedDesktopIconView::invalidPoint() const ++{ ++ return QPoint(-1, -1); ++} ++ ++QPoint AdvancedDesktopIconView::getGridPosFromMousePos(const QPoint &mousePos) ++{ ++ int x = mousePos.x()/m_gridSize.width(); ++ if (layoutDirection() == Qt::RightToLeft) { ++ int vewportX = viewport()->rect().topRight().x(); ++ x = (vewportX - mousePos.x())/m_gridSize.width(); ++ } ++ return QPoint(x, mousePos.y()/m_gridSize.height()); ++} ++ ++bool AdvancedDesktopIconView::gridPosLesserThan(const QPoint &leftPos, const QPoint &rightPos) ++{ ++ if (leftPos.x() < rightPos.x()) ++ return true; ++ ++ if (leftPos.x() > rightPos.x()) ++ return false; ++ ++ if (leftPos.y() < rightPos.y()) ++ return true; ++ return false; ++} ++const QStringList AdvancedDesktopIconView::getSelections() ++{ ++ QStringList uris; ++ auto indexes = selectionModel()->selection().indexes(); ++ for (auto index : indexes) { ++ uris<<index.data(Qt::UserRole).toString(); ++ } ++ uris.removeDuplicates(); ++ return uris; ++} ++ ++const QStringList AdvancedDesktopIconView::getAllFileUris() ++{ ++ QStringList uris; ++ for (int i = 0; i < m_proxy_model->rowCount(); i++) { ++ auto index = m_proxy_model->index(i, 0); ++ uris<<index.data(Qt::UserRole).toString(); ++ } ++ return uris; ++} ++ ++const int AdvancedDesktopIconView::getAllDisplayFileCount() ++{ ++ if(m_proxy_model) ++ return m_proxy_model->rowCount(); ++ return 0; ++} ++ ++void AdvancedDesktopIconView::setSelections(const QStringList &uris) ++{ ++ clearSelection(); ++ for (int i = 0; i < m_proxy_model->rowCount(); i++) { ++ auto index = m_proxy_model->index(i, 0); ++ if (uris.contains(index.data(Qt::UserRole).toString())) { ++ selectionModel()->select(index, QItemSelectionModel::Select); ++ } ++ } ++} ++ ++void AdvancedDesktopIconView::invertSelections() ++{ ++ QItemSelectionModel *selectionModel = this->selectionModel(); ++ const QItemSelection currentSelection = selectionModel->selection(); ++ this->selectAll(); ++ selectionModel->select(currentSelection, QItemSelectionModel::Deselect); ++ //clearAllIndexWidgets(); ++} ++ ++void AdvancedDesktopIconView::setEditFlag(bool edit) ++{ ++ qDebug() << "setEditFlag:" <<edit; ++ m_is_edit = edit; ++ if (! m_is_edit) ++ m_edit_uri = ""; ++} ++ ++bool AdvancedDesktopIconView::getEditFlag() ++{ ++ return m_is_edit; ++} ++ ++bool AdvancedDesktopIconView::isRenaming() ++{ ++ return m_is_renaming; ++} ++ ++void AdvancedDesktopIconView::setRenaming(bool renaming) ++{ ++ m_is_renaming = renaming; ++} ++ ++int AdvancedDesktopIconView::updateBWList() ++{ ++ m_proxy_model->updateBlackAndWriteLists(); ++ /* ++ * 重新按照既定规则排序,这样可以避免出现空缺和图标重叠的情况 ++ */ ++ //不可重新排序,会丢失位置,只做检查调整空缺和重叠情况,相关bug#161875 ++ resolutionChange(); ++ viewport()->update(); ++ return 0; ++} ++ ++QString AdvancedDesktopIconView::getBlackAndWhiteModel() ++{ ++ return m_proxy_model->getBlackAndWhiteModel(); ++} ++ ++QSet<QString> AdvancedDesktopIconView::getBWListInfo() ++{ ++ return m_proxy_model->getBWListInfo(); ++} ++ ++bool AdvancedDesktopIconView::getBlackAndWhiteListExist(QString name) ++{ ++ return m_proxy_model->getBlackAndWhiteListExist(name); ++} ++ ++void AdvancedDesktopIconView::setSortType(int sortType) ++{ ++ m_resolutionItemPosHash.clear(); ++ m_itemPosHash.clear(); ++ m_autoArrange.clear(); ++ m_storageBox.clear(); ++ m_proxy_model->setSortType(sortType); ++ m_proxy_model->sort(1); ++ m_proxy_model->sort(0, Qt::SortOrder(m_proxy_model->getSortOrder())); ++ for (int i = 0; i < m_proxy_model->rowCount(); i++) { ++ auto index = m_proxy_model->index(i, 0); ++ QString uri = index.data(Qt::UserRole).toString(); ++ ++ int col = i/m_availableRowCount; ++ int row = i%m_availableRowCount; ++ if (col >= m_availableColumnCount) { ++ row = 0; ++ col = 0; ++ m_storageBox.append(uri); ++ } ++ QPoint pos(col, row); ++ model()->setData(index, pos, PositionRole); ++ setFileMetaInfoPos(uri, pos); ++ m_autoArrange.append(uri); ++ } ++} ++ ++int AdvancedDesktopIconView::getSortType() ++{ ++ return m_proxy_model->getSortType(); ++} ++ ++void AdvancedDesktopIconView::scrollToSelection(const QString &uri) ++{ ++ ++} ++ ++int AdvancedDesktopIconView::getSortOrder() ++{ ++ return m_proxy_model->getSortOrder(); ++} ++ ++void AdvancedDesktopIconView::setSortOrder(int sortOrder) ++{ ++ m_resolutionItemPosHash.clear(); ++ m_itemPosHash.clear(); ++ m_autoArrange.clear(); ++ m_storageBox.clear(); ++ m_proxy_model->setSortOrder(sortOrder); ++ m_proxy_model->sort(0, Qt::SortOrder(sortOrder)); ++ for (int i = 0; i < m_proxy_model->rowCount(); i++) { ++ auto index = m_proxy_model->index(i, 0); ++ QString uri = index.data(Qt::UserRole).toString(); ++ ++ int col = i/m_availableRowCount; ++ int row = i%m_availableRowCount; ++ if (col >= m_availableColumnCount) { ++ row = 0; ++ col = 0; ++ m_storageBox.append(uri); ++ } ++ QPoint pos(col, row); ++ model()->setData(index, pos, PositionRole); ++ setFileMetaInfoPos(uri, pos); ++ m_autoArrange.append(uri); ++ } ++} ++ ++void AdvancedDesktopIconView::editUri(const QString &uri) ++{ ++ clearAllIndexWidgets(); ++ qDebug() << "editUri clearAllIndexWidgets"; ++ auto origin = FileUtils::getOriginalUri(uri); ++ QTimer::singleShot(100, this, [=]() { ++ auto index = m_proxy_model->mapFromSource(m_model->indexFromUri(origin)); ++ edit(index); ++ qDebug() << "editUri index:"<<index<<uri; ++ }); ++} ++ ++void AdvancedDesktopIconView::editUris(const QStringList uris) ++{ ++ clearAllIndexWidgets(); ++ auto origin = FileUtils::getOriginalUri(uris.first()); ++ QTimer::singleShot(100, this, [=]() { ++ edit(m_proxy_model->mapFromSource(m_model->indexFromUri(origin))); ++ }); ++} ++ ++ ++void AdvancedDesktopIconView::UpdateToEditUris(QStringList uris) ++{ ++ m_uris_to_edit = uris; ++} ++ ++void AdvancedDesktopIconView::setCutFiles(const QStringList &uris) ++{ ++ ClipboardUtils::setClipboardFiles(uris, true); ++ this->viewport()->update(); ++} ++ ++void AdvancedDesktopIconView::closeView() ++{ ++ deleteLater(); ++} ++void AdvancedDesktopIconView::fileCreated(const QString &uri) ++{ ++// qDebug()<<"DesktopIconView::fileCreated,view:" << this <<m_new_files_to_be_selected.length(); ++// if (m_new_files_to_be_selected.isEmpty()) { ++// m_new_files_to_be_selected<<uri; ++ ++// QTimer::singleShot(500, this, [=]() { ++// qDebug() << "m_new_files_to_be_selected isEmpty:"<<this->state(); ++// if (this->state() & QAbstractItemView::EditingState) ++// return; ++ ++// if (! this->m_uris_to_edit.isEmpty()) ++// return; ++// qDebug() << "fileCreated setSelections"<<m_new_files_to_be_selected.length(); ++// this->setSelections(m_new_files_to_be_selected); ++// m_new_files_to_be_selected.clear(); ++// }); ++// } else { ++// if (!m_new_files_to_be_selected.contains(uri)) { ++// m_new_files_to_be_selected<<uri; ++// } ++// } ++ ++ if (m_proxy_model->getDesktopUseAutoLayout()) { ++ //自动排序情况下如果有重复名字导致替换后出现空位,需要重新排序 ++ clearAllIndexWidgets(); ++ bool haveEmptyPosition = checkEmptyPositon(); ++ if (haveEmptyPosition) { ++ m_autoArrange = layoutItems(); ++ } ++ } ++ /* 新建文件/文件夹,可编辑文件名,copy时不能编辑 */ ++ //fix bug#164160, use same way as mainwindow ++ if(this->m_uris_to_edit.isEmpty()) ++ return; ++ ++ QString editUri = Peony::FileUtils::urlDecode(this->m_uris_to_edit.first()); ++ QString infoUri = Peony::FileUtils::urlDecode(uri); ++ qDebug() << "fileCreated editUri:"<<editUri<<infoUri; ++ if (editUri == infoUri ) { ++ QTimer::singleShot(100, this, [=]() { ++ this->editUri(uri); ++ m_edit_uri = uri; ++ }); ++ } ++ this->m_uris_to_edit.clear(); ++} ++ ++bool AdvancedDesktopIconView::dragToOtherScreen(QDropEvent *event) ++{ ++ // 从其它视图拖拽到此视图空白区域 ++ auto view = qobject_cast<AdvancedDesktopIconView*>(event->source()); ++ if (this != event->source() && view) { ++ QPoint pressedGridPos = view->getGridPosFromMousePos(view->m_pressedPos); ++ QPoint dropedGridPos = getGridPosFromMousePos(event->pos()); ++ m_pressedPos = QPoint(-1,-1); ++ view->m_pressedPos = QPoint(-1,-1); ++ m_rubberBand->setVisible(false); ++ auto gridOffset = dropedGridPos - pressedGridPos; ++ qDebug()<<gridOffset; ++ ++ QHash<QString, QModelIndex> relayoutIndexes; ++ QModelIndexList dragIndex = view->selectedIndexes(); ++ for (QModelIndex index : dragIndex) { ++ QString uri = index.data(UriRole).toString(); ++ bool ok = false; ++ auto pos = getIndexGridPos(index, &ok); ++ if (ok && !isInvalidPoint(pos)) { ++ pos += gridOffset; ++ if (isInvalidPoint(pos) || !m_itemPosHash.keys(pos).isEmpty()) { ++ relayoutIndexes.insert(uri, index); ++ continue; ++ } ++ } else { ++ relayoutIndexes.insert(uri, index); ++ continue; ++ } ++ m_itemPosHash.insert(uri, pos); ++ view->m_itemPosHash.remove(uri); ++ qDebug()<<" ----------- "<<m_itemPosHash << index << uri << "============" <<index; ++ view->model()->setData(index, pos, PositionRole); ++ view->model()->setData(index, m_id, ScreenIdRole); ++ setFileMetaInfoPos(uri, pos); ++ } ++ ++ for (QString uri : relayoutIndexes.keys()) { ++ QPoint pos = findNextEmptyGridPos(dropedGridPos); ++ if (!isInvalidPoint(pos)) { ++ m_itemPosHash.insert(uri, pos); ++ qDebug()<<" ----------- "<<m_itemPosHash; ++ auto index = relayoutIndexes.value(uri); ++ view->model()->setData(index, pos, PositionRole); ++ view->model()->setData(index, m_id, ScreenIdRole); ++ qDebug()<<" -----------relayoutIndexes "<<m_itemPosHash << index << uri; ++ setFileMetaInfoPos(uri, pos); ++ } ++ } ++ m_proxy_model->invalidate(); ++ view->m_proxy_model->invalidate(); ++ return true; ++ } ++ return false; ++} ++ ++void AdvancedDesktopIconView::mousePressEvent(QMouseEvent *e) ++{ ++ m_dragPressPos = e->pos(); ++ if (e->button() == Qt::LeftButton) { ++ m_pressedPos = e->pos(); ++ m_rubberBand->setGeometry(QRect(e->pos(), QSize())); ++ m_rubberBand->setVisible(true); ++ } else { ++ m_pressedPos = QPoint(-1,-1); ++ } ++ ++ // bug extend selection bug ++ m_real_do_edit = false; ++ ++ if (e->modifiers() & Qt::ShiftModifier) { ++ m_shift_key_pressed = true; ++ } else { ++ m_shift_key_pressed = false; ++ } ++ ++ if (e->modifiers() & Qt::ControlModifier) ++ m_ctrl_key_pressed = true; ++ else { ++ m_ctrl_key_pressed = false; ++ if (!m_shift_key_pressed) ++ m_ctrl_or_shift_pressed = false; ++ } ++ ++ auto index = indexAt(e->pos()); ++ if (!m_ctrl_or_shift_pressed) { ++ if (!index.isValid()) { ++ clearAllIndexWidgets(); ++ clearSelection(); ++ Q_EMIT clearOtherViewSelection(); ++ } else { ++ m_last_index = index; ++ //fix rename state has no menuRequest issue, bug#44107 ++ if (! m_is_edit) ++ { ++ clearAllIndexWidgets(); ++ Q_EMIT clearOtherViewSelection(); ++ //force to recreate new DesktopIndexWidget, to fix not show name issue ++ if (indexWidget(m_last_index)) ++ setIndexWidget(m_last_index, nullptr); ++ auto indexWidget = new DesktopIndexWidget(qobject_cast<DesktopIconViewDelegate *>(itemDelegate()), viewOptions(), m_last_index); ++ setIndexWidget(m_last_index, ++ indexWidget); ++ indexWidget->move(visualRect(m_last_index).topLeft()); ++ } ++ } ++ } ++ ++ //qDebug()<<m_last_index.data(); ++ if (e->button() != Qt::LeftButton) { ++ // fix #115384, context menu key issue ++ if (e->button() == Qt::RightButton) { ++ auto index = indexAt(e->pos()); ++ if (!selectedIndexes().contains(index)) { ++ selectionModel()->select(index, QItemSelectionModel::SelectCurrent); ++ } ++ } ++ return; ++ } ++ ++ if (e->button() == Qt::LeftButton && e->modifiers() & Qt::ControlModifier && selectedIndexes().contains(index)) { ++ m_noSelectOnPress = true; ++ } else { ++ m_noSelectOnPress = false; ++ } ++ ++ QAbstractItemView::mousePressEvent(e); ++} ++ ++void AdvancedDesktopIconView::mouseReleaseEvent(QMouseEvent *event) ++{ ++ m_pressedPos = QPoint(-1, -1); ++ m_rubberBand->setVisible(false); ++ m_noSelectOnPress = false; ++ QAbstractItemView::mouseReleaseEvent(event); ++ this->viewport()->update(viewport()->rect()); ++} ++ ++void AdvancedDesktopIconView::mouseMoveEvent(QMouseEvent *event) ++{ ++ QAbstractItemView::mouseMoveEvent(event); ++ ++ QModelIndex itemIndex = indexAt(event->pos()); ++ if (!itemIndex.isValid()) { ++ if (QToolTip::isVisible()) { ++ QToolTip::hideText(); ++ } ++ } ++ ++ auto tmpRect = QRect(event->pos(), m_pressedPos).normalized(); ++ if (m_pressedPos.x() >= 0 && m_pressedPos.y() >= 0 && m_rubberBand->isVisible() && state() != QAbstractItemView::DraggingState) { ++ tmpRect.translate(viewportMargins().left(), viewportMargins().top()); ++ m_rubberBand->setGeometry(tmpRect); ++ setState(QAbstractItemView::DragSelectingState); ++ } ++ ++ // ugly, 需要优化 ++ if (state() == QAbstractItemView::DragSelectingState) ++ setSelection(tmpRect, selectionCommand(indexAt(event->pos()), event)); ++} ++ ++void AdvancedDesktopIconView::mouseDoubleClickEvent(QMouseEvent *event) ++{ ++ if (! GlobalSettings::getInstance()->getValue(ENABLE_DOUBLE_CLICK_DESKTOP).toBool()) ++ return; ++ ++ QAbstractItemView::mouseDoubleClickEvent(event); ++ m_real_do_edit = false; ++} ++ ++void AdvancedDesktopIconView::dragEnterEvent(QDragEnterEvent *e) ++{ ++ m_real_do_edit = false; ++ m_isDragging = true; ++ auto action = m_ctrl_key_pressed ? Qt::CopyAction : Qt::MoveAction; ++ qDebug()<<"drag enter event" <<action; ++ if (e->mimeData()->hasUrls()) { ++ if (FileUtils::containsStandardPath(e->mimeData()->urls())) { ++ e->ignore(); ++ return; ++ } ++ e->setDropAction(action); ++ e->accept(); ++ //e->acceptProposedAction(); ++ } ++} ++ ++void AdvancedDesktopIconView::dragMoveEvent(QDragMoveEvent *e) ++{ ++ m_real_do_edit = false; ++ if (e->keyboardModifiers() & Qt::ControlModifier) ++ m_ctrl_key_pressed = true; ++ else ++ m_ctrl_key_pressed = false; ++ ++ auto action = m_ctrl_key_pressed ? Qt::CopyAction : Qt::MoveAction; ++ auto index = indexAt(e->pos()); ++ if (index.isValid() && index != m_last_index) { ++ QHoverEvent he(QHoverEvent::HoverMove, e->posF(), e->posF()); ++ viewportEvent(&he); ++ } else { ++ QHoverEvent he(QHoverEvent::HoverLeave, e->posF(), e->posF()); ++ viewportEvent(&he); ++ } ++ if (/*e->source() == this &&*/ m_proxy_model->getDesktopUseAutoLayout()) { ++ if (index.isValid()) { ++ auto oldRect = m_dropIndicatorRect; ++ auto rect = visualRect(index); ++ int y = (m_gridSize.height() - rect.height())/2 + 1; ++ if (e->pos().y() - rect.top() <= 5) { ++ m_dropIndicatorRect = rect; ++ m_dropIndicatorRect.setBottom(rect.top() + 1); ++ m_dropIndicatorRect.translate(0, -y); ++ m_dropIndicatorPos = DropIndicatorPosition::AboveItem; ++ } else if (rect.bottom() - e->pos().y() <= 20) { ++ m_dropIndicatorRect = rect; ++ m_dropIndicatorRect.setTop(rect.bottom() - 1); ++ m_dropIndicatorRect.translate(0, y); ++ m_dropIndicatorPos = DropIndicatorPosition::BelowItem; ++ } else { ++ m_dropIndicatorRect = QRect(); ++ m_dropIndicatorPos = DropIndicatorPosition::OnItem; ++ } ++ if (oldRect != m_dropIndicatorRect) ++ viewport()->update(rect.adjusted(0, -100, 0, 100)); ++ } else { ++ if (m_dropIndicatorRect.isValid()) { ++ viewport()->update(m_dropIndicatorRect.adjusted(0, -100, 0, 100)); ++ } ++ m_dropIndicatorRect = QRect(); ++ m_dropIndicatorPos = DropIndicatorPosition::OnViewport; ++ } ++ } ++ e->setDropAction(action); ++ if (e->isAccepted()) ++ return; ++ qDebug()<<"drag move event" <<action; ++ if (this == e->source()) { ++ e->accept(); ++ return QAbstractItemView::dragMoveEvent(e); ++ } ++ e->accept(); ++} ++ ++void AdvancedDesktopIconView::dragLeaveEvent(QDragLeaveEvent *e) ++{ ++ m_isDragging = false; ++ QAbstractItemView::dragLeaveEvent(e); ++} ++ ++bool AdvancedDesktopIconView::viewportEvent(QEvent *e) ++{ ++ switch (e->type()) { ++ case QEvent::HoverMove: ++ case QEvent::HoverEnter: ++ onEntered(indexAt(static_cast<QHoverEvent*>(e)->pos())); ++ break; ++ case QEvent::HoverLeave: ++ onEntered(QModelIndex()); ++ break; ++ case QEvent::Leave: ++ onEntered(QModelIndex()); // If we've left, no hover should be needed anymore ++ m_rubberBand->setVisible(false); ++ break; ++ default: ++ break; ++ } ++ return QAbstractItemView::viewportEvent(e); ++} ++ ++QItemSelectionModel::SelectionFlags AdvancedDesktopIconView::selectionCommand(const QModelIndex &index, const QEvent *event) const ++{ ++ if (!event) ++ return QAbstractItemView::selectionCommand(index, event); ++ ++ if (event->type() == QEvent::MouseButtonPress) { ++ auto e = static_cast<const QMouseEvent *>(event); ++ if (e->button() == Qt::LeftButton && e->modifiers() & Qt::ControlModifier && selectedIndexes().contains(index)) { ++ return QItemSelectionModel::NoUpdate; ++ } ++ } else if (event->type() == QEvent::MouseButtonRelease) { ++ auto e = static_cast<const QMouseEvent *>(event); ++ if (e->button() == Qt::LeftButton && e->modifiers() & Qt::ControlModifier) { ++ QItemSelectionModel::SelectionFlags flags; ++ if (m_noSelectOnPress) { ++ flags = QItemSelectionModel::Deselect; ++ } ++ return flags; ++ } ++ } ++ return QAbstractItemView::selectionCommand(index, event); ++} ++ ++void AdvancedDesktopIconView::zoomOut() ++{ ++ clearAllIndexWidgets(); ++ switch (zoomLevel()) { ++ case Huge: ++ Q_EMIT setZoomLevel(Large); ++ break; ++ case Large: ++ Q_EMIT setZoomLevel(Normal); ++ break; ++ case Normal: ++ Q_EMIT setZoomLevel(Small); ++ break; ++ default: ++ //setDefaultZoomLevel(zoomLevel()); ++ break; ++ } ++} ++ ++void AdvancedDesktopIconView::zoomIn() ++{ ++ clearAllIndexWidgets(); ++ switch (zoomLevel()) { ++ case Small: ++ Q_EMIT setZoomLevel(Normal); ++ break; ++ case Normal: ++ Q_EMIT setZoomLevel(Large); ++ break; ++ case Large: ++ Q_EMIT setZoomLevel(Huge); ++ break; ++ default: ++ //setDefaultZoomLevel(zoomLevel()); ++ break; ++ } ++} ++ ++void AdvancedDesktopIconView::setDefaultZoomLevel(ZoomLevel level) ++{ ++ //qDebug()<<"set default zoom level:"<<level; ++ m_zoom_level = level; ++ m_margin = QPoint(10, 5); ++ switch (level) { ++ case Small: ++ m_margin *= 0.8; ++ setIconSize(QSize(24, 24)); ++ break; ++ case Large: ++ m_margin *= 1.2; ++ setIconSize(QSize(64, 64)); ++ break; ++ case Huge: ++ m_margin *= 1.4; ++ setIconSize(QSize(96, 96)); ++ break; ++ default: ++ m_zoom_level = Normal; ++ setIconSize(QSize(48, 48)); ++ break; ++ } ++ ++ auto metaInfo = FileMetaInfo::fromUri("computer:///"); ++ if (metaInfo) { ++ qDebug()<<"set zoom level"<<m_zoom_level; ++ metaInfo->setMetaInfoInt("peony-qt-desktop-zoom-level", int(m_zoom_level)); ++ } ++} ++ ++AdvancedDesktopIconView::ZoomLevel AdvancedDesktopIconView::zoomLevel() const ++{ ++ //FIXME: ++ if (m_zoom_level != Invalid) ++ return m_zoom_level; ++ ++ auto metaInfo = FileMetaInfo::fromUri("computer:///"); ++ if (metaInfo) { ++ auto i = metaInfo->getMetaInfoInt("peony-qt-desktop-zoom-level"); ++ return ZoomLevel(i); ++ } ++ ++ GFile *computer = g_file_new_for_uri("computer:///"); ++ g_file_query_info_async(computer, ++ "metadata::peony-qt-desktop-zoom-level", ++ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, ++ G_PRIORITY_DEFAULT, ++ nullptr, ++ GAsyncReadyCallback(queryZoomLevelAsyncCallback), ++ const_cast<AdvancedDesktopIconView *>(this)); ++ g_object_unref(computer); ++ return Normal; ++} ++ ++void AdvancedDesktopIconView::clearAllIndexWidgets(const QStringList &uris) ++{ ++ if (!model()) ++ return; ++ m_hoverIndex = QModelIndex(); ++ //fix bug#164160, when edit new file, infoUpdate call clearAllIndexWidgets issue ++ if (m_is_edit && uris.length()>0 && m_edit_uri == uris.first()) ++ return; ++ ++ if(uris.length()>0 ) ++ qDebug() << "clearAllIndexWidgets uris:"<<uris.first()<<uris.length(); ++ ++ int row = 0; ++ auto index = model()->index(row, 0); ++ while (index.isValid()) { ++ if (uris.isEmpty() || uris.contains(index.data(Qt::UserRole).toString())) { ++ auto widget = indexWidget(index); ++ if (widget) { ++ widget->hide(); ++ } ++ setIndexWidget(index, nullptr); ++ //qDebug() << "clearAllIndexWidgets setIndexWidget"<<index; ++ } ++ row++; ++ index = model()->index(row, 0); ++ } ++ ++ // avoid dirty region out of index visual rect. ++ // link to: #77272. ++ viewport()->update(); ++} ++ ++void AdvancedDesktopIconView::setFileMetaInfoPos(const QString &uri, const QPoint &pos) ++{ ++ m_itemPosHash.insert(uri, pos); ++ QPoint currentPos(pos.x()*m_gridSize.width(), pos.y()*m_gridSize.height()); ++ QRect rect(mapToGlobal(currentPos)*qApp->devicePixelRatio(), m_gridSize*qApp->devicePixelRatio()); ++ FileInfo::fromUri(uri).get()->setProperty("iconGeometry", rect); ++} ++ ++void AdvancedDesktopIconView::resolutionChange() ++{ ++ qInfo()<<"start resolutionChange()"; ++ QSize screenSize = this->viewport()->size(); ++ // do not relayout items while screen size is empty. ++ if (screenSize.isEmpty()) { ++ qWarning()<<"screen size is not available"; ++ return; ++ } ++ ++ if (m_itemPosHash.isEmpty()) { ++ return; ++ } ++ if (m_proxy_model && m_proxy_model->getDesktopUseAutoLayout()) { ++ // setSortType(int(DesktopItemProxyModel::SortType::RelatedPos)); ++ relayoutExsitingItemsAndUpdate(m_autoArrange); ++ return; ++ } ++ QStringList needChanged; ++ for (QString uri : m_itemPosHash.keys()) { ++ QPoint pos = m_itemPosHash.value(uri); ++ if (isInvalidPoint(pos)) { ++ needChanged.append(uri); ++ if (!m_resolutionItemPosHash.contains(uri)) { ++ // remember item position before resolution changed. ++ m_resolutionItemPosHash.insert(uri, pos); ++ } ++ } ++ } ++ qInfo()<<"need changed item"<<needChanged << " resolution item" << m_resolutionItemPosHash; ++ ++ if (!needChanged.isEmpty()) { ++ qInfo()<<"屏幕过小,有元素超过屏幕范围" ; ++ relayoutExsitingItems(needChanged); ++ } else { ++ qInfo()<<"没有元素超过屏幕范围, 尝试恢复超过屏幕范围的元素" <<m_resolutionItemPosHash; ++ QStringList itemNeedBeRelayout; ++ for (auto uri : m_resolutionItemPosHash.keys()) { ++ auto pos = m_resolutionItemPosHash.value(uri); ++ if (isInvalidPoint(pos)) { ++ itemNeedBeRelayout.append(uri); ++ } else { ++ m_resolutionItemPosHash.remove(uri); ++ m_itemPosHash.insert(uri, pos); ++ } ++ } ++ relayoutExsitingItems(itemNeedBeRelayout); ++ } ++ ++ for (int row = 0; row < model()->rowCount(); row++) { ++ // todo: 桌面整理时的表现 ++ auto index = model()->index(row, 0, QModelIndex()); ++ auto uri = index.data(UriRole).toString(); ++ auto pos = m_itemPosHash.value(uri); ++ model()->setData(index, pos, PositionRole); ++ setFileMetaInfoPos(uri, pos); ++ if (m_resolutionItemPosHash.contains(uri)) { ++ QPoint reolutionItemPos = m_resolutionItemPosHash.value(uri); ++ QStringList exceptedPos; ++ exceptedPos<<QString::number(reolutionItemPos.x()); ++ exceptedPos<<QString::number(reolutionItemPos.y()); ++ exceptedPos<<QString::number(m_id); ++ model()->setData(index, exceptedPos, ExceptedPositionRole); ++ } ++ } ++} ++ ++void AdvancedDesktopIconView::relayoutExsitingItems(const QStringList &uris) ++{ ++ if (uris.isEmpty() || m_itemPosHash.isEmpty()) { ++ return; ++ } ++ for (auto uri : uris) { ++ m_itemPosHash.remove(uri); ++ } ++ auto allFileUris = getAllFileUris(); ++ ++ QPoint startPos = QPoint(); ++ int i = 0; ++ for (; i < uris.count(); i++) { ++ QString uri = uris[i]; ++ if (!allFileUris.contains(uri)) ++ continue; ++ startPos = findNextEmptyGridPos(startPos); ++ if (isInvalidPoint(startPos)) { ++ break; ++ } ++ setFileMetaInfoPos(uri, startPos); ++ m_storageBox.removeOne(uri); ++ } ++ ++ for(; i < uris.count(); i++) { ++ QString uri = uris[i]; ++ setFileMetaInfoPos(uri, QPoint(0, 0)); ++ if (!m_storageBox.contains(uri)) { ++ m_storageBox.append(uri); ++ } ++ } ++} ++ ++static bool iconSizeLessThan (const QPair<QPoint, QString>& p1, const QPair<QPoint, QString>& p2) ++{ ++ if (p1.first.x() > p2.first.x()) ++ return false; ++ ++ if (p1.first.x() < p2.first.x()) ++ return true; ++ ++ if ((p1.first.x() == p2.first.x())) ++ return p1.first.y() < p2.first.y(); ++ ++ return true; ++} ++ ++QStringList AdvancedDesktopIconView::layoutItems(bool closest) ++{ ++ if (!closest) { ++ QHash<QString, QPoint> extendMap; ++ QHash<QString, QPoint> primaryItemMap; ++ QList<QPair<QPoint, QString>> newPosition; ++ QStringList overItem; ++ for (int row = 0; row < model()->rowCount(); row++) { ++ // todo: 桌面整理时的表现 ++ auto index = model()->index(row, 0, QModelIndex()); ++ auto uri = index.data(UriRole).toString(); ++ QPoint pos = index.data(PositionRole).toPoint(); ++ auto extendPos = index.data(ExtendScreenPositionRole).toStringList(); ++ if (3 == extendPos.count() && 0 != extendPos[2].toInt()) { ++ extendMap.insert(uri, pos); ++ } else { ++ newPosition << QPair<QPoint, QString>(pos, uri); ++ primaryItemMap.insert(uri, pos); ++ } ++ } ++ ++ for (QString uri : extendMap.keys()) { ++ QPoint pos = extendMap.value(uri); ++ if (primaryItemMap.keys(pos).isEmpty()) { ++ newPosition << QPair<QPoint, QString>(pos, uri); ++ } else { ++ overItem.append(uri); ++ } ++ } ++ ++ if (!newPosition.isEmpty()) { ++ ++ std::stable_sort(newPosition.begin(), newPosition.end(), iconSizeLessThan); ++ ++ QStringList sortedUris; ++ for (auto pair : newPosition) { ++ if (m_storageBox.contains(pair.second)) { ++ overItem.prepend(pair.second); ++ } else { ++ sortedUris.append(pair.second); ++ } ++ } ++ sortedUris << overItem; ++ sortedUris.removeDuplicates(); ++ qDebug() <<"--------------------layoutItems" <<sortedUris; ++ relayoutExsitingItemsAndUpdate(sortedUris); ++ return sortedUris; ++ } ++ } else { ++ qInfo()<<"not supported yet"; ++ } ++ return QStringList(); ++} ++ ++void AdvancedDesktopIconView::relayoutExsitingItemsAndUpdate(const QStringList uris) ++{ ++ if (m_availableColumnCount == 1 && m_availableRowCount == 1) ++ return; ++ m_itemPosHash.clear(); ++ m_autoArrange.clear(); ++ m_storageBox.clear(); ++ int index = 0; ++ //按照行和列顺序放置uri ++ for (int column = 0; column < m_availableColumnCount && index < uris.size(); column++) { ++ for (int row = 0; row < m_availableRowCount && index < uris.size(); row++) { ++ auto uri = uris[index]; ++ QPoint pos = QPoint(column, row); ++ setFileMetaInfoPos(uri, pos); ++ m_autoArrange.append(uri); ++ index++; ++ } ++ } ++ ++ for (; index < uris.size(); index++) { ++ //满屏暂时都放到(0,0)位置 ++ auto uri = uris[index]; ++ QPoint pos = QPoint(0, 0); ++ setFileMetaInfoPos(uri, pos); ++ m_autoArrange.append(uri); ++ m_storageBox.append(uri); ++ } ++ ++ m_autoArrange.removeDuplicates(); ++ m_proxy_model->invalidate(); ++ for (int row = 0; row < model()->rowCount(); row++) { ++ // todo: 桌面整理时的表现 ++ auto index = model()->index(row, 0, QModelIndex()); ++ auto uri = index.data(UriRole).toString(); ++ if(!uris.contains(uri)) ++ continue; ++ auto pos = m_itemPosHash.value(uri); ++ model()->setData(index, pos, PositionRole); ++ } ++ qDebug() << "AdvancedDesktopIconView::relayoutExsitingItemsAndUpdat---------" <<m_itemPosHash; ++ ++ viewport()->update(); ++} ++ ++void AdvancedDesktopIconView::checkItemsOver() ++{ ++ QStringList primaryItem, extendItem; ++ QStringList needRelayoutItems; ++ for (int i = 0; i < model()->rowCount(); i++) { ++ QModelIndex index = model()->index(i, 0); ++ QString uri = index.data(Qt::UserRole).toString(); ++ QPoint pos = index.data(PositionRole).toPoint(); ++ auto overUris = m_itemPosHash.keys(pos); ++ if (overUris.count() > 1) { ++ auto extendPos = index.data(ExtendScreenPositionRole).toStringList(); ++ if (3 == extendPos.count() && 0 != extendPos[2].toInt()) { ++ extendItem.append(uri); ++ } else { ++ primaryItem.append(uri); ++ } ++ } ++ } ++ primaryItem<<extendItem; ++ for (auto uri : primaryItem) { ++ if (needRelayoutItems.contains(uri)) { ++ continue; ++ } ++ auto pos = m_itemPosHash.value(uri); ++ auto list = m_itemPosHash.keys(pos); ++ list.removeAll(uri); ++ needRelayoutItems << list; ++ } ++ ++ needRelayoutItems.removeDuplicates(); ++ qDebug() << "checkitemsover" <<needRelayoutItems; ++ ++ if (0 == needRelayoutItems.size()) { ++ return; ++ } ++ ++ relayoutExsitingItems(needRelayoutItems); ++ ++ for (int row = 0; row < model()->rowCount(); row++) { ++ // todo: 桌面整理时的表现 ++ auto index = model()->index(row, 0, QModelIndex()); ++ auto uri = index.data(UriRole).toString(); ++ auto pos = m_itemPosHash.value(uri); ++ model()->setData(index, pos, PositionRole); ++ setFileMetaInfoPos(uri, pos); ++ } ++} ++ ++GAsyncReadyCallback AdvancedDesktopIconView::queryZoomLevelAsyncCallback(GObject *obj, GAsyncResult *res, AdvancedDesktopIconView *p_this) ++{ ++ GError *err = nullptr; ++ auto info = g_file_query_info_finish(G_FILE(obj), res, &err); ++ ++ if (info) { ++ char* zoom_level = g_file_info_get_attribute_as_string(info, "metadata::peony-qt-desktop-zoom-level"); ++ if (!zoom_level) { ++ //qDebug()<<"======================no zoom level meta info\n\n\n"; ++ g_object_unref(info); ++ p_this->setDefaultZoomLevel(Normal); ++ return nullptr; ++ } ++ g_object_unref(info); ++ QString zoomLevel = zoom_level; ++ g_free(zoom_level); ++ int level =(zoomLevel.toInt()) == Invalid? Normal: ZoomLevel(QString(zoomLevel).toInt()); ++ p_this->setDefaultZoomLevel(ZoomLevel(level)); ++ return nullptr; ++ } else { ++ if (err) { ++ qDebug()<<err->code<<err->message; ++ g_error_free(err); ++ } ++ p_this->setDefaultZoomLevel(Normal); ++ return nullptr; ++ } ++} ++ ++bool AdvancedDesktopIconView::isFull() ++{ ++ //bug#123045 主屏桌面空间不足,新建的文件未放置到扩展屏桌面上 ++ qDebug() << "colNum:" <<m_availableColumnCount << "rowNum:"<< m_availableRowCount << "total:" << m_proxy_model->rowCount(); ++ for (int column = 0; column < m_availableColumnCount; column++) { ++ for (int row = 0; row < m_availableRowCount; row++) { ++ QPoint pos = QPoint(column, row); ++ if (m_itemPosHash.keys(pos).isEmpty()) ++ return false; ++ } ++ } ++ return true; ++} ++ ++void AdvancedDesktopIconView::setShowHidden() ++{ ++ m_show_hidden = !GlobalSettings::getInstance()->getValue(SHOW_HIDDEN_PREFERENCE).toBool(); ++ m_proxy_model->setShowHidden(m_show_hidden); ++ //fix show hidden file desktop icons overlapped issue ++ //QTimer::singleShot(100, this, [=]() { ++ //resetAllItemPositionInfos(); ++ //refresh(); ++ //}); ++ //fix#181595 桌面图标设置隐藏后排序 ++// Q_EMIT updateView(); ++ m_proxy_model->invalidate(); ++ showHiddenFile(); ++} ++ ++void AdvancedDesktopIconView::showHiddenFile() ++{ ++ if (m_proxy_model && m_proxy_model->getDesktopUseAutoLayout()) { ++ relayoutExsitingItemsAndUpdate(m_autoArrange); ++ } ++ viewport()->update(); ++ return; ++} ++ ++QPoint AdvancedDesktopIconView::checkGridPos(const QPoint pos) ++{ ++ if (!isInvalidPoint(pos)) { ++ return pos; ++ } ++ int col = pos.x() >= m_availableColumnCount ? m_availableColumnCount-1 : pos.x(); ++ int row = pos.y() >= m_availableRowCount ? m_availableRowCount-1 : pos.y(); ++ QPoint newPos = findNextEmptyGridPos(QPoint(col, row)); ++ return newPos; ++} ++ ++void AdvancedDesktopIconView::onEntered(const QModelIndex &index) ++{ ++ if (m_hoverIndex == index) ++ return; ++ ++ if (m_hoverIndex.isValid()) { ++ const QRect rect = visualRect(m_hoverIndex); ++ if (viewport()->rect().intersects(rect)) ++ viewport()->update(rect); ++ } ++ ++ if (index.isValid()) { ++ const QRect rect = visualRect(index); ++ if (viewport()->rect().intersects(rect)) ++ viewport()->update(rect); ++ } ++ ++ m_hoverIndex = index; ++} ++ ++bool AdvancedDesktopIconView::checkEmptyPositon() ++{ ++ if (m_autoArrange.isEmpty()) ++ return false; ++ for (int col = 0; col < m_availableColumnCount; col++) { ++ for (int row = 0; row < m_availableRowCount; row++) { ++ if (col * m_availableRowCount + row + 1 > m_autoArrange.count()) { ++ return false; ++ } ++ if (m_itemPosHash.keys(QPoint(col, row)).isEmpty()) { ++ return true; ++ } ++ } ++ } ++ ++ return false; ++} ++ ++bool AdvancedDesktopIconView::execSharedFileLink(const QString uri) ++{ ++ auto info = FileInfo::fromUri(uri); ++ if (info->isEmptyInfo()) { ++ FileInfoJob j(info); ++ j.querySync(); ++ } ++ if (uri.endsWith(".desktop")) { ++ GKeyFile* key_file = g_key_file_new(); ++ QUrl url = uri; ++ QString desktopfp = url.path(); ++ g_key_file_load_from_file(key_file, desktopfp.toUtf8().constData(), G_KEY_FILE_KEEP_COMMENTS, nullptr); ++ GError* error = NULL; ++ if (g_key_file_has_key(key_file, G_KEY_FILE_DESKTOP_GROUP, "X-Peony-CMD", nullptr)) { ++ if (g_key_file_has_key(key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, nullptr)) { ++ g_autofree char* val = g_key_file_get_value(key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, &error); ++ if (error) { ++ qWarning() << "get desktop file:" << uri << " name error:" << error->code << " -- " << error->message; ++ g_error_free(error); ++ error = nullptr; ++ } else { ++#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) ++ QProcess p; ++ p.setProgram("peony"); ++ QString str = val; ++ str = str.replace("peony ",""); ++ p.setArguments(QStringList() << str); ++ qint64 pid; ++ p.startDetached(&pid); ++ // send startinfo to kwindowsystem ++ quint32 timeStamp = QX11Info::isPlatformX11() ? QX11Info::appUserTime() : 0; ++ KStartupInfoId startInfoId; ++ startInfoId.initId(KStartupInfo::createNewStartupIdForTimestamp(timeStamp)); ++ startInfoId.setupStartupEnv(); ++ KStartupInfoData data; ++ data.setHostname(); ++ data.addPid(pid); ++#ifdef KSTARTUPINFO_HAS_SET_ICON_GEOMETRY ++ QRect rect = info.get()->property("iconGeometry").toRect(); ++ if (rect.isValid()) { ++ data.setIconGeometry(rect); ++ } ++#endif ++ data.setLaunchedBy(getpid()); ++ KStartupInfo::sendStartup(startInfoId, data); ++#else ++ QProcess p; ++ QString strq; ++ for (int i = 0;i < uri.length();++i) { ++ if(uri[i] == ' '){ ++ strq += "%20"; ++ }else{ ++ strq += uri[i]; ++ } ++ } ++ p.startDetached("/usr/bin/peony", QStringList()<<strq<<"%U&"); ++#endif ++ return true; ++ } ++ } ++ } ++ } ++ return false; ++} ++ ++int AdvancedDesktopIconView::radius() const ++{ ++ return m_radius; ++} ++ +diff --git a/peony-qt-desktop/advanced-desktop-icon-view.h b/peony-qt-desktop/advanced-desktop-icon-view.h +new file mode 100644 +index 0000000..04c2a11 +--- /dev/null ++++ b/peony-qt-desktop/advanced-desktop-icon-view.h +@@ -0,0 +1,361 @@ ++/* ++ * Peony-Qt ++ * ++ * Copyright (C) 2024, KylinSoft Co., Ltd. ++ * ++ * 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 3 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, see <https://www.gnu.org/licenses/>. ++ * ++ * Authors: yangyanwei <yangyanwei@kylinos.cn> ++ * ++ */ ++ ++#ifndef ADVANCEDDESKTOPICONVIEW_H ++#define ADVANCEDDESKTOPICONVIEW_H ++ ++#include <QAbstractItemView> ++#include <QHash> ++#include <gio/gio.h> ++ ++#include "directory-view-plugin-iface.h" ++#include "peony-dbus-service.h" ++ ++#include <QStandardPaths> ++#include <QTimer> ++ ++#include <QMap> ++ ++class QRubberBand; ++class QGSettings; ++ ++namespace Peony { ++class AdvancedDesktopItemModel; ++class DesktopItemProxyModel; ++class DesktopWindow; ++class DesktopIndexWidget; ++class DesktopIconViewDelegate; ++class FileItemModel; ++class PeonyDbusService; ++} ++ ++class AdvancedDesktopIconView : public QAbstractItemView, public Peony::DirectoryViewIface ++{ ++ friend class Peony::DesktopWindow; ++ friend class Peony::DesktopIndexWidget; ++ friend class Peony::DesktopIconViewDelegate; ++ friend class Peony::AdvancedDesktopItemModel; ++ Q_OBJECT ++public: ++ enum ZoomLevel { ++ Invalid, ++ Small, //icon: 24x24; grid: 64x64; hover rect: 60x60; font: system*0.8 ++ Normal, //icon: 48x48; grid: 96x96; hover rect = 90x90; font: system ++ Large, //icon: 64x64; grid: 115x135; hover rect = 105x118; font: system*1.2 ++ Huge //icon: 96x96; grid: 140x170; hover rect = 120x140; font: system*1.4 ++ ++ }; ++ Q_ENUM(ZoomLevel) ++ ++ enum Direction { ++ All, ++ Right, ++ Bottom ++ }; ++ Q_ENUM(Direction) ++ ++ enum DesktopIconRole { ++ UriRole = Qt::UserRole, ++ PositionRole = Qt::UserRole + 2, ++ ScreenIdRole = Qt::UserRole + 3, ++ ExceptedPositionRole = Qt::UserRole + 4, ++ SinglesSreenPositionRole = Qt::UserRole + 5, ++ ExtendScreenPositionRole = Qt::UserRole + 6, ++ IsFloatItemRole = Qt::UserRole + 7 ++ }; Q_ENUM (DesktopIconRole) ++ ++ explicit AdvancedDesktopIconView(QWidget *parent = nullptr); ++ ~AdvancedDesktopIconView(); ++ void initShoutCut(); ++ ++ void openFileByUri(QString uri); ++ void initDoubleClick(); ++ ++ void bindModel(Peony::FileItemModel *sourceModel, Peony::FileItemProxyFilterSortModel *proxyModel) { ++ Q_UNUSED(sourceModel) Q_UNUSED(proxyModel) ++ } ++ void setProxy(Peony::DirectoryViewProxyIface *proxy) { ++ Q_UNUSED(proxy) ++ } ++ ++ const QString viewId() { ++ return tr("Desktop Icon View"); ++ } ++ ++ //location ++ const QString getDirectoryUri() { ++ return "file://" + QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); ++ } ++ ++ ++ QRect visualRect(const QModelIndex &index) const override; ++ void scrollTo(const QModelIndex &index, ScrollHint hint) override; ++ QModelIndex indexAt(const QPoint &point) const override; ++ ++ //selections ++ const QStringList getSelections(); ++ ++ //children ++ const QStringList getAllFileUris(); ++ const int getAllDisplayFileCount(); ++ void UpdateToEditUris(QStringList uris); ++ ZoomLevel zoomLevel() const; ++ void setEditFlag(bool edit); ++ bool getEditFlag(); ++ ++ bool isRenaming(); ++ void setRenaming(bool renaming); ++ int updateBWList(); ++ QString getBlackAndWhiteModel(); ++ QSet<QString> getBWListInfo(); ++ bool getBlackAndWhiteListExist(QString name); ++ void clearAllIndexWidgets(const QStringList &uris = QStringList()); ++ ++ void setId(int id); ++ bool isFull(); ++ ++ int radius() const; ++ ++private: ++ bool execSharedFileLink(const QString uri); ++ ++Q_SIGNALS: ++ void updateView(); ++ void sig_fileCreated(const QString &uri); ++ void clearOtherViewSelection(); ++ void setZoomLevel(ZoomLevel level); ++ ++public Q_SLOTS: ++ ++ //location ++ void open(const QStringList &uris, bool newWindow) {} ++ void setDirectoryUri(const QString &uri) {} ++ void beginLocationChange() {} ++ void stopLocationChange() {} ++ void closeView(); ++ ++ //selections ++ void setSelections(const QStringList &uris); ++ void invertSelections(); ++ void scrollToSelection(const QString &uri); ++ ++ //clipboard ++ void setCutFiles(const QStringList &uris); ++ ++ Peony::DirectoryViewProxyIface *getProxy() { ++ return nullptr; ++ } ++ ++ void setSortType(int sortType); ++ ++ void setSortOrder(int sortOrder); ++ ++ //edit ++ void editUri(const QString &uri); ++ ++ int getSortType(); ++ int getSortOrder(); ++ ++ void editUris(const QStringList uris); ++ void fileCreated(const QString &uri); ++ ++ void setDefaultZoomLevel(ZoomLevel level); ++ void zoomIn(); ++ void zoomOut(); ++ ++ /** ++ * @brief layoutItems ++ * 自动排列 ++ * @param closest ++ * @return ++ * 返回自动排列的uri列表 ++ */ ++ QStringList layoutItems(bool closest = false); ++ ++ /** ++ * @brief relayoutExsitingItemsAndUpdate ++ * 设置自动排列后, ++ * @param uris ++ */ ++ void relayoutExsitingItemsAndUpdate(const QStringList uris); ++ ++ void setShowHidden(); ++ void showHiddenFile(); ++ ++protected: ++ QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override; ++ int horizontalOffset() const override; ++ int verticalOffset() const override; ++ bool isIndexHidden(const QModelIndex &index) const override; ++ void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command) override; ++ QRegion visualRegionForSelection(const QItemSelection &selection) const override; ++ ++ QStyleOptionViewItem viewOptions() const override; ++ void startDrag(Qt::DropActions supportedActions) override; ++ ++ void reset() override; ++ void rowsInserted(const QModelIndex &parent, int start, int end) override; ++ void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) override; ++ ++ void paintEvent(QPaintEvent *e) override; ++ void resizeEvent(QResizeEvent *e) override; ++ void mousePressEvent(QMouseEvent *event) override; ++ void mouseMoveEvent(QMouseEvent *event) override; ++ void mouseReleaseEvent(QMouseEvent *event) override; ++ void mouseDoubleClickEvent(QMouseEvent *event); ++ ++ //void ensureItemPosByUri(const QString &uri); ++ void dragEnterEvent(QDragEnterEvent *e) override; ++ void dragMoveEvent(QDragMoveEvent *e) override; ++ void dragLeaveEvent(QDragLeaveEvent *e) override; ++ void dropEvent(QDropEvent *e) override; ++ bool viewportEvent(QEvent *e) override; ++ ++ QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex &index, const QEvent *event) const override; ++ ++ bool dropWhenAotoArrange(QDropEvent *event); ++ bool dragToOtherScreen(QDropEvent *event); ++ ++ void wheelEvent(QWheelEvent *e); ++ void keyPressEvent(QKeyEvent *e); ++ void keyReleaseEvent(QKeyEvent *e); ++ ++ void checkItemsOver(); ++ static GAsyncReadyCallback queryZoomLevelAsyncCallback(GObject *obj, ++ GAsyncResult *res, ++ AdvancedDesktopIconView *p_this); ++ ++private Q_SLOTS: ++ void recalculateAvailableRowAndColumnCount(); ++ void onEntered(const QModelIndex &index); ++ ++public: ++ int getViewAvailableRowCount(); ++ int getViewAvailableColumnCount(); ++ ++ void setMargins(); ++ /*! ++ * \brief getIndexGridPos ++ * 获取当前index所在的网格位置 ++ * \param index ++ * \param ok ++ * 如果成功获取则设置为true,否则设置为false ++ * \return ++ * index所在的网格位置,如果不成功则返回(-1,-1) ++ */ ++ QPoint getIndexGridPos(const QModelIndex &index, bool *ok) const; ++ ++ /*! ++ * \brief findNextEmptyGridPos ++ * 获取下一个空网格所在的位置 ++ * \return ++ * ++ */ ++ QPoint findNextEmptyGridPos(const QPoint &startGridPos = QPoint()); ++ ++ bool isInvalidPoint(const QPoint &point); ++ QPoint invalidPoint() const; ++ ++ /*! ++ * \brief getGridPosFromMousePos ++ * 根据当前鼠标事件传入的位置返回网格的位置 ++ * \param mousePos ++ * \return ++ * 鼠标位置对应的网格位置 ++ */ ++ QPoint getGridPosFromMousePos(const QPoint &mousePos); ++ ++ /*! ++ * \brief gridPosLesserThan ++ * 从左上到右下比较两个网格位置的顺序 ++ * \param leftPos ++ * \param rightPos ++ * \return ++ */ ++ bool gridPosLesserThan(const QPoint &leftPos, const QPoint &rightPos); ++ void refresh(); ++ ++ void setFileMetaInfoPos(const QString &uri, const QPoint &pos); ++ void relayoutExsitingItems(const QStringList &uris); ++ void resolutionChange(); ++ QPoint checkGridPos(const QPoint pos); ++ bool checkEmptyPositon(); ++ ++private: ++ ZoomLevel m_zoom_level = Invalid; ++ QSize m_gridSize = QSize(); ++ int m_availableRowCount = 1; ++ int m_availableColumnCount = 1; ++ QRubberBand *m_rubberBand = nullptr; ++ QPoint m_pressedPos = QPoint(-1,-1); ++ QPoint m_dragPressPos; ++ /*! ++ * \brief m_itemPosMap ++ * 当前视图中的文件uri和对应所在的网格位置 ++ */ ++ QHash<QString, QPoint> m_itemPosHash; ++ QStringList m_autoArrange; ++ QHash<QString, QPoint> m_resolutionItemPosHash; ++ QStringList m_storageBox; ++// QVector<bool> m_itemStatus; ++ QModelIndex m_last_index; ++ QTimer m_edit_trigger_timer; ++ ++ QStringList m_new_files_to_be_selected; ++ QStringList m_uris_to_edit;/* 新建文件/文件夹,可编辑文件名list */ ++ QString m_edit_uri; /* 正在编辑的文件,信息更新不重置*/ ++ ++ // bool m_is_refreshing = false; ++ ++ bool m_real_do_edit = false; ++ ++ bool m_ctrl_or_shift_pressed = false; ++ ++ bool m_ctrl_key_pressed = false; ++ ++ bool m_shift_key_pressed = false; ++ ++ bool m_show_hidden; ++ ++ bool m_is_renaming = false; ++ ++ bool m_is_edit = false; ++ ++ bool m_initialized = false; ++ bool m_isDragging = false; ++ ++ bool m_noSelectOnPress = false; ++ ++ Peony::AdvancedDesktopItemModel *m_model = nullptr; ++ Peony::DesktopItemProxyModel *m_proxy_model = nullptr; ++ QGSettings *m_panelSetting = nullptr; ++ Peony::PeonyDbusService *m_peonyDbusSer = nullptr; ++ int m_id = 0; ++ QRect m_dropIndicatorRect; ++ DropIndicatorPosition m_dropIndicatorPos = OnViewport; ++ QPersistentModelIndex m_hoverIndex; ++ QPoint m_margin; ++ ++ int m_radius = 6; ++}; ++ ++#endif // ADVANCEDDESKTOPICONVIEW_H +diff --git a/peony-qt-desktop/advanced-desktop-item-model.cpp b/peony-qt-desktop/advanced-desktop-item-model.cpp +new file mode 100644 +index 0000000..ac34f23 +--- /dev/null ++++ b/peony-qt-desktop/advanced-desktop-item-model.cpp +@@ -0,0 +1,1036 @@ ++/* ++ * Peony-Qt ++ * ++ * Copyright (C) 2024, KylinSoft Co., Ltd. ++ * ++ * 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 3 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, see <https://www.gnu.org/licenses/>. ++ * ++ * Authors: yangyanwei <yangyanwei@kylinos.cn> ++ * ++ */ ++ ++#include "advanced-desktop-item-model.h" ++ ++#include "file-enumerator.h" ++#include "file-info.h" ++#include "file-info-job.h" ++#include "file-info-manager.h" ++#include "file-watcher.h" ++#include "file-operation-manager.h" ++#include "file-move-operation.h" ++#include "file-trash-operation.h" ++#include "file-copy-operation.h" ++#include "file-operation-utils.h" ++ ++#include "thumbnail-manager.h" ++#include "usershare-manager.h" ++ ++#include "file-meta-info.h" ++ ++#include "peony-desktop-application.h" ++#include "advanced-desktop-icon-view.h" ++#include "global-settings.h" ++#include "sound-effect.h" ++#ifdef KY_SDK_SOUND_EFFECTS ++#include "ksoundeffects.h" ++#endif ++#include "desktop-icon-view-delegate.h" ++#include "desktop-menu-plugin-manager.h" ++#include "emblem-provider.h" ++#include "desktop-window-manager.h" ++#include "tooltips-manager.h" ++ ++#include <QStandardPaths> ++#include <QIcon> ++ ++#include <QMimeData> ++#include <QUrl> ++ ++#include <QTimer> ++ ++#include <QMessageBox> ++ ++#include <QDebug> ++ ++#include "common.h" ++using namespace Peony; ++#ifdef KY_SDK_SOUND_EFFECTS ++using namespace kdk; ++#endif ++ ++static bool initMetaInfo = false; ++ ++AdvancedDesktopIconItem::AdvancedDesktopIconItem(const QString &text) ++ : QStandardItem(text) ++{ ++ setData(text, AdvancedDesktopIconView::UriRole); ++ auto metaInfo = FileMetaInfo::fromUri(text); ++ if (metaInfo) { ++ QStringList strPos = metaInfo->getMetaInfoStringListV1(ITEM_GRID_POS_ATTRIBUTE); ++ qDebug() << "[AdvancedDesktopIconItem::AdvancedDesktopIconItem]"<<strPos <<text; ++ if (2 == strPos.count() && strPos[0] >= 0 && strPos[1] >= 0) { ++ QPoint pos(strPos[0].toInt(), strPos[1].toInt()); ++ setData(pos, AdvancedDesktopIconView::PositionRole); ++ int id = metaInfo->getMetaInfoInt(SCREEN_ID); ++ setData(id, AdvancedDesktopIconView::ScreenIdRole); ++ } else { ++ int id; ++ QPoint currentPos; ++ PeonyDesktopApplication::getDesktopWindowManager()->createIdAndPos(&id, ¤tPos); ++ setData(id, AdvancedDesktopIconView::ScreenIdRole); ++ setData(currentPos, AdvancedDesktopIconView::PositionRole); ++ } ++ QStringList value = metaInfo->getMetaInfoStringList(RESTORE_ITEM_GRID_POS_ATTRIBUTE); ++ setData(value, AdvancedDesktopIconView::ExceptedPositionRole); ++ value = metaInfo->getMetaInfoStringList(RESTORE_EXTEND_ITEM_GRID_POS_ATTRIBUTE); ++ setData(value, AdvancedDesktopIconView::ExtendScreenPositionRole); ++ value = metaInfo->getMetaInfoStringList(RESTORE_SINGLESCREEN_ITEM_GRID_POS_ATTRIBUTE); ++ setData(value, AdvancedDesktopIconView::SinglesSreenPositionRole); ++ } ++} ++ ++ ++AdvancedDesktopIconItem::AdvancedDesktopIconItem(const QString &text, bool fileCreate) ++ : QStandardItem(text) ++{ ++ setData(text, AdvancedDesktopIconView::UriRole); ++ auto metaInfo = FileMetaInfo::fromUri(text); ++ if (metaInfo) { ++ qDebug() << "[AdvancedDesktopIconItem::AdvancedDesktopIconItem]"<<text; ++ QStringList strPos = metaInfo->getMetaInfoStringListV1(ITEM_GRID_POS_ATTRIBUTE); ++ int currentId = metaInfo->getMetaInfoInt(SCREEN_ID); ++ auto manager = PeonyDesktopApplication::getDesktopWindowManager(); ++ bool isRenaming = manager->getIconView(currentId)->isRenaming(); ++ ++ qDebug() << "[AdvancedDesktopIconItem::AdvancedDesktopIconItem]"<<strPos <<text; ++ if (isRenaming && 2 == strPos.count() && strPos[0] >= 0 && strPos[1] >= 0) { ++ manager->getIconView(currentId)->setRenaming(false); ++ QPoint pos(strPos[0].toInt(), strPos[1].toInt()); ++ setData(pos, AdvancedDesktopIconView::PositionRole); ++ setData(currentId, AdvancedDesktopIconView::ScreenIdRole); ++ QStringList value = metaInfo->getMetaInfoStringList(RESTORE_ITEM_GRID_POS_ATTRIBUTE); ++ setData(value, AdvancedDesktopIconView::ExceptedPositionRole); ++ value = metaInfo->getMetaInfoStringList(RESTORE_EXTEND_ITEM_GRID_POS_ATTRIBUTE); ++ setData(value, AdvancedDesktopIconView::ExtendScreenPositionRole); ++ value = metaInfo->getMetaInfoStringList(RESTORE_SINGLESCREEN_ITEM_GRID_POS_ATTRIBUTE); ++ setData(value, AdvancedDesktopIconView::SinglesSreenPositionRole); ++ } else { ++ int id; ++ QPoint currentPos; ++ manager->createIdAndPos(&id, ¤tPos); ++ setData(id, AdvancedDesktopIconView::ScreenIdRole); ++ setData(currentPos, AdvancedDesktopIconView::PositionRole); ++ QStringList value(""); ++ setData(value, AdvancedDesktopIconView::ExceptedPositionRole); ++ setData(value, AdvancedDesktopIconView::ExtendScreenPositionRole); ++ setData(value, AdvancedDesktopIconView::SinglesSreenPositionRole); ++ } ++ } ++} ++ ++void AdvancedDesktopIconItem::setData(const QVariant &value, int role) ++{ ++ switch(role){ ++ case AdvancedDesktopIconView::ScreenIdRole:{ ++ QString uri = data(AdvancedDesktopIconView::UriRole).toString(); ++ auto metaInfo = FileMetaInfo::fromUri(uri); ++ if (metaInfo) { ++ int id = value.toInt(); ++ metaInfo->setMetaInfoInt(SCREEN_ID, id); ++ } ++ break; ++ } ++ case AdvancedDesktopIconView::PositionRole:{ ++ QString uri = data(AdvancedDesktopIconView::UriRole).toString(); ++ auto metaInfo = FileMetaInfo::fromUri(uri); ++ if (metaInfo) { ++ QPoint pos = value.toPoint(); ++ QStringList itemPos; ++ itemPos<<QString::number(pos.x()); ++ itemPos<<QString::number(pos.y()); ++ metaInfo->setMetaInfoStringListV1(ITEM_GRID_POS_ATTRIBUTE, itemPos); ++ } ++ break; ++ } ++ case AdvancedDesktopIconView::ExceptedPositionRole:{ ++ QString uri = data(AdvancedDesktopIconView::UriRole).toString(); ++ auto metaInfo = FileMetaInfo::fromUri(uri); ++ if (metaInfo) { ++ QStringList restorePos = value.toStringList(); ++ metaInfo->setMetaInfoStringList(RESTORE_ITEM_GRID_POS_ATTRIBUTE, restorePos); ++ } ++ break; ++ } ++ case AdvancedDesktopIconView::SinglesSreenPositionRole:{ ++ QString uri = data(AdvancedDesktopIconView::UriRole).toString(); ++ auto metaInfo = FileMetaInfo::fromUri(uri); ++ if (metaInfo) { ++ QStringList singlesScreenPos = value.toStringList(); ++ metaInfo->setMetaInfoStringList(RESTORE_SINGLESCREEN_ITEM_GRID_POS_ATTRIBUTE, singlesScreenPos); ++ } ++ break; ++ } ++ case AdvancedDesktopIconView::ExtendScreenPositionRole:{ ++ QString uri = data(AdvancedDesktopIconView::UriRole).toString(); ++ auto metaInfo = FileMetaInfo::fromUri(uri); ++ if (metaInfo) { ++ QStringList extendScreenPos = value.toStringList(); ++ metaInfo->setMetaInfoStringList(RESTORE_EXTEND_ITEM_GRID_POS_ATTRIBUTE, extendScreenPos); ++ } ++ } ++ } ++ QStandardItem::setData(value, role); ++} ++ ++AdvancedDesktopItemModel::AdvancedDesktopItemModel(QObject *parent) ++ : QStandardItemModel(parent) ++{ ++ // do not redo layout new items while we start an operation with peony's api. ++ connect(FileOperationManager::getInstance(), &FileOperationManager::operationStarted, this, [=](){ ++ m_items_need_relayout.clear(); ++ }); ++ ++ m_thumbnail_watcher = std::make_shared<FileWatcher>("thumbnail:///, this"); ++ ++ connect(m_thumbnail_watcher.get(), &FileWatcher::fileChanged, this, [=](const QString &uri) { ++ auto index = indexFromUri(uri); ++ if (index.isValid()) ++ Q_EMIT this->dataChanged(index, index); ++ }); ++ ++ m_trash_watcher = std::make_shared<FileWatcher>("trash:///", this); ++ ++ this->connect(m_trash_watcher.get(), &FileWatcher::fileCreated, [=](const QString &uri) { ++ //qDebug()<<"trash changed"; ++ auto trash = FileInfo::fromUri("trash:///"); ++ auto job = new FileInfoJob(trash); ++ job->setAutoDelete(); ++ connect(job, &FileInfoJob::infoUpdated, [=]() { ++ auto trashIndex = this->indexFromUri("trash:///"); ++ this->dataChanged(trashIndex, trashIndex); ++ Q_EMIT this->requestClearIndexWidget(QStringList()<<uri); ++ }); ++ job->queryAsync(); ++ }); ++ ++ this->connect(m_trash_watcher.get(), &FileWatcher::fileDeleted, [=](const QString &uri) { ++ //qDebug()<<"trash changed"; ++ auto trash = FileInfo::fromUri("trash:///"); ++ auto job = new FileInfoJob(trash); ++ job->setAutoDelete(); ++ connect(job, &FileInfoJob::infoUpdated, [=]() { ++ auto trashIndex = this->indexFromUri("trash:///"); ++ this->dataChanged(trashIndex, trashIndex); ++ Q_EMIT this->requestClearIndexWidget(QStringList()<<uri); ++ }); ++ job->queryAsync(); ++ }); ++ ++ // monitor desktop ++ QString desktopFile = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); ++ ++ qWarning() << "desktopfile:" << desktopFile; ++ m_desktop_watcher = std::make_shared<FileWatcher>("file://" + desktopFile, this); ++ m_desktop_watcher->setMonitorChildrenChange(true); ++ m_desktop_watcher->connect(m_desktop_watcher.get(), &FileWatcher::fileCreated, this, &AdvancedDesktopItemModel::fileCreated); ++ ++ m_desktop_watcher->connect(m_desktop_watcher.get(), &FileWatcher::fileDeleted, [=](const QString &uri) { ++ for (auto info : m_files) { ++ if (info->uri() == uri) { ++ int row = m_files.indexOf(info); ++ removeRow(row, QModelIndex()); ++ qDebug() <<"AdvancedDesktopIconView::rowsAboutToBeRemoved" <<row; ++ m_files.removeOne(info); ++ Q_EMIT sig_relayoutItems(); ++ ++ if (info->isDir()) { ++ QString displayName = info->displayName(); ++ if (UserShareInfoManager::getInstance()->getUsershareLists().contains(displayName)) { ++ SharedDeleteInfoThread *thread = new SharedDeleteInfoThread(info->uri()); ++ connect(thread, &SharedDeleteInfoThread::finished, thread, &SharedDeleteInfoThread::deleteLater); ++ thread->start(); ++ } ++ } ++ } ++ } ++ }); ++ ++ m_desktop_watcher->connect(m_desktop_watcher.get(), &FileWatcher::fileChanged, [=](const QString &uri) { ++ ++ for (auto info : m_files) { ++ if (info->uri() == uri) { ++ auto job = new FileInfoJob(info); ++ job->setAutoDelete(); ++ connect(job, &FileInfoJob::infoUpdated, this, [=]() { ++ //file changed, force create thubnail, link tobug#83108 ++ ThumbnailManager::getInstance()->createThumbnail(uri, m_thumbnail_watcher, true); ++ this->dataChanged(indexFromUri(uri), indexFromUri(uri)); ++ Q_EMIT this->requestClearIndexWidget(QStringList()<<uri); ++ ++ // task #355895 ++ // 触发更新异常软链接,如果一直异常,则会进入轮询知道查询到正常信息 ++ if (info->isSymbolLink() && !info->canRead()) { ++ this->pendingQuery(uri); ++ } ++ }); ++ job->queryAsync(); ++ this->dataChanged(indexFromUri(uri), indexFromUri(uri)); ++ return; ++ } ++ } ++ }); ++ ++ //handle standard dir changing. ++ m_dir_manager =new UserdirManager(this); ++ //refresh after standard dir changed. ++ connect(m_dir_manager,&UserdirManager::desktopDirChanged,[=](){ ++ refresh(); ++ }); ++ connect(m_dir_manager,&UserdirManager::thumbnailSetingChange,[=](){ ++ refresh(); ++ }); ++ ++ connect(FileOperationManager::getInstance(), &FileOperationManager::operationStarted, this, [=](std::shared_ptr<FileOperationInfo> info){ ++ if (info.get()->m_type == FileOperationInfo::Rename) { ++ m_renaming_operation_info = info; ++ auto renamingUri = info.get()->m_src_uris.first(); ++ if (!renamingUri.endsWith(".desktop")) { ++ m_renaming_operation_info = nullptr; ++ return; ++ } ++ m_renaming_file_pos.first = renamingUri; ++ m_renaming_file_pos.second = indexFromUri(renamingUri).data(AdvancedDesktopIconView::PositionRole).toPoint(); ++ } else { ++ m_renaming_file_pos.first = nullptr; ++ m_renaming_file_pos.second = QPoint(); ++ m_renaming_operation_info = nullptr; ++ } ++ }); ++ connect(FileOperationManager::getInstance(), &FileOperationManager::operationFinished, this, [=](std::shared_ptr<FileOperationInfo> info){ ++ if (info.get()->m_type == FileOperationInfo::Rename) { ++ if (!info.get()->m_has_error) { ++ auto renamingUri = info.get()->target(); ++ if (!renamingUri.endsWith(".desktop")) { ++ m_renaming_operation_info = nullptr; ++ m_renaming_file_pos.first = nullptr; ++ m_renaming_file_pos.second = QPoint(); ++ return; ++ } ++ QPoint target_pos = indexFromUri(renamingUri).data(AdvancedDesktopIconView::PositionRole).toPoint(); ++ //desktop文件重命名时,如果存在相同文件则不会重命名成功。由于该文件uri不会变,所以pos不变,无需更新pos ++ if (target_pos.isNull() || (m_renaming_file_pos.second == target_pos)) { ++ //desktop文件重命名成功 ++ m_renaming_file_pos.first = renamingUri; ++ m_items_need_relayout.removeOne(renamingUri); ++ m_items_need_relayout.removeOne(renamingUri + ".desktop"); ++ auto index = indexFromUri(renamingUri); ++ setData(index, m_renaming_file_pos.second, AdvancedDesktopIconView::PositionRole); ++ } else { ++ //desktop文件(uri)重命名失败 ++ QString &src_uri = info->m_src_uris.first(); ++ Q_EMIT selectUri(src_uri); ++ } ++ } else { ++ // restore/relayout? ++ } ++ m_renaming_operation_info = nullptr; ++ QTimer::singleShot(100, this, [=]{ ++ m_renaming_file_pos.first = nullptr; ++ m_renaming_file_pos.second = QPoint(); ++ }); ++ } else { ++ m_renaming_file_pos.first = nullptr; ++ m_renaming_file_pos.second = QPoint(); ++ m_renaming_operation_info = nullptr; ++ } ++ }); ++ ++ auto settings = GlobalSettings::getInstance(); ++ m_showFileExtension = settings->isExist(SHOW_FILE_EXTENSION)? settings->getValue(SHOW_FILE_EXTENSION).toBool(): true; ++ connect(GlobalSettings::getInstance(), &GlobalSettings::valueChanged, this, [=] (const QString& key) { ++ if (SHOW_FILE_EXTENSION == key) { ++ m_showFileExtension= GlobalSettings::getInstance()->getValue(key).toBool(); ++ beginResetModel(); ++ endResetModel(); ++ } ++ }); ++ ++ connect(DesktopMenuPluginManager::getInstance(), &DesktopMenuPluginManager::pluginLoadFinished, this, [=](){ ++ QTimer::singleShot(1000, this, [=]{ ++ for (auto file : m_files) { ++ EmblemProviderManager::getInstance()->queryAsync(file->uri()); ++ } ++ }); ++ }); ++ UserShareInfoManager::getInstance(); ++ ++ connect(EmblemProviderManager::getInstance(), &EmblemProviderManager::requestUpdateFile, this, [=](const QString &uri){ ++ auto index = indexFromUri(uri); ++ if (index.isValid()) ++ Q_EMIT this->dataChanged(index, index); ++ }); ++} ++ ++AdvancedDesktopItemModel::~AdvancedDesktopItemModel() ++{ ++ ++} ++ ++bool findProgram(const QString &program) ++{ ++ QFileInfo fi(program); ++ if (!program.isEmpty() && fi.isExecutable()) { ++ return true; ++ } ++ ++ const QStringList paths = QFile::decodeName(qgetenv("PATH")).split(':'); ++ for(const QString &dir : paths) { ++ QFileInfo fi= QFileInfo(dir + QDir::separator() + program); ++ if (fi.isExecutable()) { ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++void AdvancedDesktopItemModel::refreshInternal() ++{ ++ m_items_need_relayout.clear(); ++ ThumbnailManager::getInstance()->syncThumbnailPreferences(); ++ beginResetModel(); ++ for (auto info : m_files) { ++ ThumbnailManager::getInstance()->releaseThumbnail(info->uri()); ++ } ++ m_files.clear(); ++ m_enumerator = new FileEnumerator(this); ++ connect(this, &AdvancedDesktopItemModel::prepareRefresh, m_enumerator, &FileEnumerator::cancel, Qt::DirectConnection); ++ m_enumerator->setAutoDelete(); ++ QString desktopUri = "file://" + QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); ++ m_enumerator->setEnumerateDirectory(desktopUri); ++ m_enumerator->connect(m_enumerator, &FileEnumerator::enumerateFinished, this, &AdvancedDesktopItemModel::onEnumerateFinished); ++ m_enumerator->enumerateAsync(); ++ endResetModel(); ++} ++ ++QVariant AdvancedDesktopItemModel::data(const QModelIndex &index, int role) const ++{ ++ if (!index.isValid()) ++ return QVariant(); ++ ++ //qDebug()<<"data"<<m_files.at(index.row())->uri(); ++ auto info = m_files.at(index.row()); ++ switch (role) { ++ case Qt::DisplayRole:{ ++ QString displayName = info->displayName(); ++ if (info->isDesktopFile()) ++ { ++ displayName = FileUtils::handleDesktopFileName(info->uri(), info->displayName()); ++ return QVariant(displayName); ++ } ++ /* story#8359 【文件管理器】手动开启关闭文件拓展名 */ ++ if(!m_showFileExtension){ ++ if (info->isDir()) { ++ return QVariant(displayName); ++ } ++ return QVariant(FileUtils::getBaseNameOfFile(displayName)); ++ ++ }else ++ return QVariant(displayName); ++ } ++ case Qt::ToolTipRole: { ++ /** ++ * @task #346285: 【Tooltips specification】Change the desktop icon (including the management-desktop-application icon) tooltips display, ++ * add supplementary text ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-09-25 ++ */ ++ return QVariant(TooltipsManagerInstance.generateTooltip(info.get())); ++ } ++ case Qt::DecorationRole: { ++ if (!info->isExistTargetOfSymlink()) { ++ return QIcon::fromTheme("unknown"); ++ } ++ auto thumbnail = ThumbnailManager::getInstance()->tryGetThumbnail(info->uri()); ++ if (!thumbnail.isNull()) { ++ return thumbnail; ++ } ++ return QIcon::fromTheme(info->iconName(), QIcon::fromTheme("unknown")); ++ } ++ case UriRole: ++ return info->uri(); ++ case IsLinkRole: ++ return info->isSymbolLink(); ++ default: ++ QStandardItem *item = itemFromIndex(index); ++ return item ? item->data(role) : QVariant(); ++ } ++ return QVariant(); ++} ++ ++std::shared_ptr<FileInfo> AdvancedDesktopItemModel::getFileInfo(const QModelIndex &index) const ++{ ++ if (!index.isValid() || m_files.isEmpty() || index.row() >= m_files.length()) ++ return nullptr; ++ ++ return m_files.at(index.row()); ++} ++ ++void AdvancedDesktopItemModel::onEnumerateFinished(bool successed) ++{ ++ if (!successed) { ++ qWarning()<<"failed to enumerate desktop"; ++ beginResetModel(); ++ m_files.clear(); ++ endResetModel(); ++ return; ++ } ++ ++ // check if there is info querying, if true, wait querying finished. ++ while (!m_querying_files.isEmpty()) { ++ qApp->processEvents(); ++ } ++ ++ if (m_files.count() > 0) { ++ beginRemoveRows(QModelIndex(), 0, m_files.count() - 1); ++ m_files.clear(); ++ endRemoveRows(); ++ } ++ ++ auto computer = FileInfo::fromUri("computer:///"); ++ auto personal = FileInfo::fromPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); ++ auto trash = FileInfo::fromUri("trash:///"); ++ ++ QList<std::shared_ptr<FileInfo>> infos; ++ ++ infos<<computer; ++ infos<<trash; ++ infos<<personal; ++ ++ infos<<m_enumerator->getChildren(); ++ m_querying_files = infos; ++ m_files = infos; ++ ++ for (auto info : infos) { ++ auto asyncJob = new FileInfoJob(info); ++ connect(this, &AdvancedDesktopItemModel::prepareRefresh, asyncJob, [=]{ ++ asyncJob->cancel(); ++ asyncJob->setProperty("isCancelled", true); ++ }, Qt::DirectConnection); ++ connect(asyncJob, &FileInfoJob::queryAsyncFinished, this, [=](bool successed){ ++ m_querying_files.removeOne(info); ++ if (!successed) { ++ m_files.removeOne(info); ++ } ++ ++ if (asyncJob->property("isCancelled").toBool()) { ++ // quit loop to avoid invalid data inserted; ++ return; ++ } ++ ++ // task #355895 ++ // 初始化时无法获取软链接源文件信息,进入轮询 ++ if (info->isSymbolLink() && !info->canRead()) { ++ this->pendingQuery(info->uri()); ++ } ++ ++ if (m_querying_files.isEmpty()) { ++ for (auto info : m_files) { ++ auto uri = info->uri(); ++ auto item = new AdvancedDesktopIconItem(info->uri()); ++ appendRow(item); ++ ++ if (info->isDesktopFile()) { ++ ThumbnailManager::getInstance()->updateDesktopFileThumbnail(info->uri(), m_thumbnail_watcher); ++ } else { ++ ThumbnailManager::getInstance()->createThumbnail(info->uri(), m_thumbnail_watcher); ++ } ++ } ++ if (!initMetaInfo) { ++ getAllRestoreInfo(); ++ initMetaInfo = true; ++ Q_EMIT emitFinish(); ++ } ++ //qDebug()<<"startMornitor"; ++ m_trash_watcher->startMonitor(); ++ ++ qWarning() << "desktopfile:" << m_desktop_watcher->currentUri() << " >>>> " << QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); ++ if (m_desktop_watcher->currentUri() != "file://" + QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)) { ++ m_desktop_watcher->stopMonitor(); ++ m_desktop_watcher->forceChangeMonitorDirectory("file://" + QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); ++ m_desktop_watcher->setMonitorChildrenChange(true); ++ } ++ m_desktop_watcher->startMonitor(); ++ ++ Q_EMIT refreshed(); ++ ++ asyncJob->deleteLater(); ++ } ++ }); ++ asyncJob->queryAsync(); ++ } ++} ++ ++const QModelIndex AdvancedDesktopItemModel::indexFromUri(const QString &uri) ++{ ++ for (auto info : m_files) { ++ if (info->uri() == uri) { ++ return index(m_files.indexOf(info), 0); ++ } ++ } ++ return QModelIndex(); ++} ++ ++QMimeData *AdvancedDesktopItemModel::mimeData(const QModelIndexList &indexes) const ++{ ++ QMimeData* data = QAbstractItemModel::mimeData(indexes); ++ //set urls data URLs correspond to the MIME type text/uri-list. ++ QList<QUrl> urls; ++ QStringList uris; ++ for (auto index : indexes) { ++ QUrl url = index.data(UriRole).toString(); ++ if (!urls.contains(url)) ++ urls<<url; ++ uris<<index.data(UriRole).toString(); ++ } ++ data->setUrls(urls); ++ auto string = uris.join(" "); ++ data->setData("peony-qt/encoded-uris", string.toUtf8()); ++ data->setText(string); ++ return data; ++} ++ ++bool AdvancedDesktopItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) ++{ ++ //qDebug()<<row<<column; ++ //qDebug()<<"drop mime data"<<parent.data()<<index(row, column, parent).data(); ++ //judge the drop dest uri. ++ QString destDirUri = nullptr; ++ if (parent.isValid()) { ++ destDirUri = parent.data(UriRole).toString(); ++ } else { ++ destDirUri = "file://" + QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); ++ } ++ ++ //if destDirUri was not set, do not execute a drop. ++ if (destDirUri.isNull()) { ++ return false; ++ } ++ ++ auto info = FileInfo::fromUri(destDirUri); ++ if (info.get()->isEmptyInfo()) { ++ // note that this case nearly won't happened. ++ // but there is a bug reported due to this. ++ // link to task #48798. ++ FileInfoJob j(info); ++ j.querySync(); ++ } ++ if (!info->isDir() && ! destDirUri.startsWith("trash:///")) { ++ return false; ++ } ++ ++ //NOTE: ++ //do not allow drop on it self. ++ auto urls = data->urls(); ++ if (urls.isEmpty()) ++ return false; ++ ++ bool hasPeonyEncodedUris = false; ++ if (data->hasFormat("peony-qt/encoded-uris")) { ++ if (!data->data("peony-qt/encoded-uris").isEmpty()) { ++ hasPeonyEncodedUris = true; ++ } ++ } ++ ++ QStringList srcUris; ++ if (hasPeonyEncodedUris) { ++ srcUris = QString(data->data("peony-qt/encoded-uris")).split(" "); ++ for (QString uri : srcUris) { ++ if (uri.startsWith("recent://")) ++ srcUris.removeOne(uri); ++ } ++ } else { ++ for (auto url : urls) { ++ //can not drag file from recent ++ if (url.url().startsWith("recent://")) ++ return false; ++ srcUris<<url.url(); ++ } ++ } ++ srcUris.removeDuplicates(); ++ ++ if (srcUris.isEmpty()) { ++ return false; ++ } ++ ++ //can not drag file to recent ++ if (destDirUri.startsWith("recent://")) ++ return false; ++ ++ //not allow drag file to itself ++ if (srcUris.contains(destDirUri)) { ++ return false; ++ } ++ ++ //can not move StandardPath to any dir ++ if (action == Qt::MoveAction && FileUtils::containsStandardPath(srcUris)) { ++ return false; ++ } ++ ++ bool bMoveFromSearchTab = false; ++ if (data->hasFormat("peony-qt/is-search")) { ++ bMoveFromSearchTab = QVariant(data->data("peony-qt/is-search")).toBool(); ++ } ++ ++ bool b_trash_item = false; ++ for(auto path : srcUris) ++ { ++ if (path.contains("trash:///")) ++ { ++ b_trash_item = true; ++ break; ++ } ++ } ++ //drag from trash to another place, return false ++ //comment to fix can not drag to copy trash file,link to bug#117741 ++// if (b_trash_item && destDirUri != "trash:///") ++// return false; ++ ++ auto fileOpMgr = FileOperationManager::getInstance(); ++ bool addHistory = true; ++ bool canNotTrash = false; ++ ++ for (auto uri : srcUris) { ++ if (uri.startsWith("filesafe:///")) { ++ canNotTrash = true; ++ break; ++ } ++ } ++ ++ if (destDirUri.startsWith("trash:///")) { ++ // 如果是保护箱删除时,不会反馈删除弹窗,保护箱文件不影响 ++ if(!(srcUris.first().startsWith("filesafe:///") && ++ (QString(srcUris.first()).remove("filesafe:///").indexOf("/") == -1))) { ++ //fix bug#91525, can trash file in U disk issue ++ FileOperationUtils::trash(srcUris, true, bMoveFromSearchTab); ++// if(canNotTrash){ ++// FileOperationUtils::trash(srcUris, false); ++// }else { ++// FileTrashOperation *trashOp = new FileTrashOperation(srcUris); ++// fileOpMgr->startOperation(trashOp, addHistory); ++// } ++ } ++ } else { ++ qDebug() << "DesktopItemModel dropMimeData:" <<action; ++ //krme files can not move to other place, default set as copy action ++ if (srcUris.first().startsWith("kmre:///") || srcUris.first().startsWith("kydroid:///")) ++ action = Qt::CopyAction; ++ ++ //filesafe files can not move to other place, default set as copy action ++ if (srcUris.first().startsWith("filesafe:///")) ++ action = Qt::CopyAction; ++ ++ //fix drag trash file to other path is copy issue,link to bug#117741 ++ if (srcUris.first().startsWith("trash:///") && action == Qt::MoveAction){ ++ //not copy move, do target move to delete file in trash ++ action = Qt::TargetMoveAction; ++ } ++ ++ auto op = FileOperationUtils::moveWithAction(srcUris, destDirUri, true, action, bMoveFromSearchTab); ++ op->connect(op, &FileOperation::operationFinished, this, [=](){ ++ //Peony::SoundEffect::getInstance()->copyOrMoveSucceedMusic(); ++ //Task#152997, use sdk play sound ++ if (op->hasError()) { ++ return; ++ } ++#ifdef KY_SDK_SOUND_EFFECTS ++ kdk::KSoundEffects::playSound(SoundType::OPERATION_FILE); ++#endif ++ }, Qt::BlockingQueuedConnection); ++ } ++ ++ //NOTE: ++ //we have to handle the dnd with file operation, so do not ++ //use QAbstractModel::dropMimeData() here; ++ return false; ++} ++ ++void AdvancedDesktopItemModel::refresh() ++{ ++ Q_EMIT prepareRefresh(); ++ ++ beginResetModel(); ++ m_files.clear(); ++ m_items_need_relayout.clear(); ++ clear(); ++ endResetModel(); ++ ++ m_desktop_info = FileInfo::fromPath(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); ++ auto infoJob = new FileInfoJob(m_desktop_info); ++ infoJob->setAutoDelete(); ++ connect(infoJob, &FileInfoJob::queryAsyncFinished, this, [=](bool successed){ ++ if (successed) { ++ refreshInternal(); ++ } else { ++ qWarning()<<"desktop model refreshs: can not query desktop info"<<m_desktop_info->uri(); ++ } ++ }); ++ connect(this, &AdvancedDesktopItemModel::prepareRefresh, infoJob, &FileInfoJob::cancel, Qt::DirectConnection); ++ infoJob->queryAsync(); ++} ++ ++void AdvancedDesktopItemModel::updateMetaInfo(QStandardItem *item) ++{ ++ //获取当前屏幕的view ++ QString uri = item->data(AdvancedDesktopIconView::UriRole).toString(); ++ auto metaInfo = FileMetaInfo::fromUri(uri); ++ if (metaInfo) { ++ int id = metaInfo->getMetaInfoInt(SCREEN_ID); ++ item->setData(id, AdvancedDesktopIconView::ScreenIdRole); ++ QStringList strPos = metaInfo->getMetaInfoStringListV1(ITEM_GRID_POS_ATTRIBUTE); ++ if (2 == strPos.count() && strPos[0] >= 0 && strPos[1] >= 0) { ++ QPoint pos(strPos[0].toInt(), strPos[1].toInt()); ++ item->setData(pos, AdvancedDesktopIconView::PositionRole); ++ } ++ QStringList value = metaInfo->getMetaInfoStringList(RESTORE_ITEM_GRID_POS_ATTRIBUTE); ++ item->setData(value, AdvancedDesktopIconView::ExceptedPositionRole); ++ value = metaInfo->getMetaInfoStringList(RESTORE_EXTEND_ITEM_GRID_POS_ATTRIBUTE); ++ item->setData(value, AdvancedDesktopIconView::ExtendScreenPositionRole); ++ value = metaInfo->getMetaInfoStringList(RESTORE_SINGLESCREEN_ITEM_GRID_POS_ATTRIBUTE); ++ item->setData(value, AdvancedDesktopIconView::SinglesSreenPositionRole); ++ } ++} ++ ++void AdvancedDesktopItemModel::getAllRestoreInfo() ++{ ++ for (int row = 0; row < rowCount(); row++) { ++ QModelIndex modelIndex = index(row, 0); ++ auto uri = modelIndex.data(AdvancedDesktopIconView::UriRole).toString(); ++ int currentId = modelIndex.data(AdvancedDesktopIconView::ScreenIdRole).toInt(); ++ QVariant tmp = modelIndex.data(AdvancedDesktopIconView::ExceptedPositionRole); ++ QStringList extendPos = tmp.toStringList(); ++ ++ if (3 == extendPos.count()) { ++ int col = extendPos.at(0).toInt(); ++ int row = extendPos.at(1).toInt(); ++ int id = extendPos.takeAt(2).toInt(); ++ if (id == currentId && col >= 0 && row >= 0) { ++ QVariant value(extendPos); ++ QPoint pos(col, row); ++ setData(modelIndex, QStringList(""), AdvancedDesktopIconView::ExceptedPositionRole); ++ setData(modelIndex, pos, AdvancedDesktopIconView::PositionRole); ++ } ++ } ++ } ++} ++ ++void AdvancedDesktopItemModel::saveExtendItemInfo() ++{ ++ bool desktopAutoLayout = GlobalSettings::getInstance()->getValue(DESKTOP_USE_AUTO_LAYOUT).toBool(); ++ QVector<IconData> iconDataList; ++ //task#74174 扩展屏的元素记录到metInfo,以便以后恢复 ++ for (int i = 0; i < rowCount(); i++) { ++ QModelIndex modelIndex = index(i, 0); ++ //QMap<int, QVariant> roleData = itemData(modelIndex); ++ auto position = modelIndex.data(AdvancedDesktopIconView::PositionRole).toPoint(); ++ int currentId = modelIndex.data(AdvancedDesktopIconView::ScreenIdRole).toInt(); ++ QStringList extendPos = modelIndex.data(AdvancedDesktopIconView::ExtendScreenPositionRole).toStringList(); ++ //恢复单屏下的图标 ++ if (desktopAutoLayout) { ++ QString uri = modelIndex.data(AdvancedDesktopIconView::UriRole).toString(); ++ iconDataList.append({currentId, position, modelIndex, uri}); ++ } else { ++ QStringList pos = modelIndex.data(AdvancedDesktopIconView::SinglesSreenPositionRole).toStringList(); ++ if (pos.count() == 2) { ++ setData(modelIndex, QPoint(pos[0].toInt(), pos[1].toInt()), AdvancedDesktopIconView::PositionRole); ++ setData(modelIndex, QStringList(), AdvancedDesktopIconView::SinglesSreenPositionRole); ++ } ++ } ++ //如果不存在扩展屏坐标,则保存当前坐标到扩展屏坐标 ++ if (extendPos.count() != 3) { ++ QStringList topLeft; ++ topLeft<<QString::number(position.x()); ++ topLeft<<QString::number(position.y()); ++ topLeft<<QString::number(currentId); ++ setData(modelIndex, topLeft, AdvancedDesktopIconView::ExtendScreenPositionRole); ++ } ++ ++ setData(modelIndex, 0, AdvancedDesktopIconView::ScreenIdRole); ++ } ++ ++ if (!iconDataList.empty()) { ++ // 排序 ++ std::sort(iconDataList.begin(), iconDataList.end()); ++ ++ int newPositionIndex = 0; ++ for (const auto &iconData : iconDataList) { ++ setData(iconData.modelIndex, QPoint(newPositionIndex, 0), AdvancedDesktopIconView::PositionRole); ++ setData(iconData.modelIndex, QStringList(), AdvancedDesktopIconView::SinglesSreenPositionRole); ++ ++newPositionIndex; ++ } ++ } ++} ++ ++void AdvancedDesktopItemModel::resetExtendItemInfo() ++{ ++ for (int row = 0; row < rowCount(); row++) { ++ QModelIndex modelIndex = index(row, 0); ++ //QMap<int, QVariant> roleData = itemData(modelIndex); ++ QVariant value = modelIndex.data(AdvancedDesktopIconView::UriRole); ++ QPoint currentPos = modelIndex.data(AdvancedDesktopIconView::PositionRole).toPoint(); ++ value = modelIndex.data(AdvancedDesktopIconView::ExtendScreenPositionRole); ++ QStringList extendPos = value.toStringList(); ++ if (3 == extendPos.count()) { ++ int col = extendPos.at(0).toInt(); ++ int row = extendPos.at(1).toInt(); ++ int id = extendPos.takeAt(2).toInt(); ++ if (col >= 0 && row >= 0) { ++ QPoint pos(col, row); ++ setData(modelIndex, pos, AdvancedDesktopIconView::PositionRole); ++ setData(modelIndex, id, AdvancedDesktopIconView::ScreenIdRole); ++ setData(modelIndex, QStringList(""), AdvancedDesktopIconView::ExtendScreenPositionRole); ++ } ++ } ++ value = modelIndex.data(AdvancedDesktopIconView::SinglesSreenPositionRole); ++ QStringList screenlist = value.toStringList(); ++ if (screenlist.count() != 2) { ++ QStringList tmp; ++ tmp<<QString::number(currentPos.x()); ++ tmp<<QString::number(currentPos.y()); ++ setData(modelIndex, tmp, AdvancedDesktopIconView::SinglesSreenPositionRole); ++ } ++ } ++} ++ ++void AdvancedDesktopItemModel::clearExtendItemPos(bool saveId) ++{ ++ //重置扩展屏和单屏坐标 ++ for (int row = 0; row < rowCount(); row++) { ++ QModelIndex modelIndex = index(row, 0); ++ //QMap<int, QVariant> roleData = itemData(modelIndex); ++ QStringList tmp(""); ++ setData(modelIndex, tmp, AdvancedDesktopIconView::SinglesSreenPositionRole); ++ if (saveId) { ++ setData(modelIndex, tmp, AdvancedDesktopIconView::ExceptedPositionRole); ++ QStringList extendPos = modelIndex.data(AdvancedDesktopIconView::ExceptedPositionRole).toStringList(); ++ if (extendPos.count() == 3) { ++ tmp.clear(); ++ tmp<<"-1"<<"-1"<<extendPos[2]; ++ } ++ } ++ setData(modelIndex, tmp, AdvancedDesktopIconView::ExtendScreenPositionRole); ++ } ++} ++ ++void AdvancedDesktopItemModel::fileCreated(const QString &uri) ++{ ++ qDebug()<<"desktop file created"<<uri; ++ ++ auto info = FileInfo::fromUri(uri); ++ bool exsited = false; ++ for (auto file : m_files) { ++ if (file->uri() == info->uri()) { ++ exsited = true; ++ break; ++ } ++ } ++ ++ if (!exsited) { ++ if (!m_renaming_file_pos.first.isEmpty() && uri != m_renaming_file_pos.first && uri.contains(m_renaming_file_pos.first)) { ++ return; ++ } ++ m_items_need_relayout.append(uri); ++ m_items_need_relayout.removeOne(m_renaming_file_pos.first); ++ m_items_need_relayout.removeOne(m_renaming_file_pos.first + ".desktop"); ++ if (m_renaming_operation_info.get()) { ++ m_items_need_relayout.removeOne(m_renaming_operation_info.get()->target()); ++ } ++ m_items_need_relayout.removeDuplicates(); ++ ++ auto job = new FileInfoJob(info); ++ job->setAutoDelete(); ++ job->querySync(); ++ ++ // locate new item ===== ++ //task#74174 扩展模式下支持拖拽图标放置到扩展屏, 创建文件获取当前view ++ ++ //file changed, force create thubnail, link tobug#83108 ++ ThumbnailManager::getInstance()->createThumbnail(info->uri(), m_thumbnail_watcher, true); ++ m_files<<info; ++ AdvancedDesktopIconItem *item = new AdvancedDesktopIconItem(info->uri(), true); ++ this->appendRow(item); ++ int id = item->data(AdvancedDesktopIconView::ScreenIdRole).toInt(); ++ auto view = PeonyDesktopApplication::getDesktopWindowManager()->getIconView(id); ++ Q_EMIT view->sig_fileCreated(uri); ++ } ++ else{ ++ //file content changed, need update fileinfo, fix bug#76908 ++ auto job = new FileInfoJob(info); ++ job->setAutoDelete(); ++ job->querySync(); ++ } ++} ++ ++void AdvancedDesktopItemModel::pendingQuery(const QString &uri) ++{ ++ // task: #355895 ++ // note: 指向外部分区的软链接在桌面初始化时可能查询不到链接的源文件, ++ // 这可能是因为外部分区挂载比桌面拉起慢导致的,这里增加一个pending机制 ++ if (!m_pending_query_timer) { ++ m_pending_query_timer = new QTimer(this); ++ m_pending_query_timer->setInterval(1000); ++ connect(m_pending_query_timer, &QTimer::timeout, this, [=]{ ++ for (auto uri : m_pending_query_uris) { ++ // 使用现有文件改变流程触发更新 ++ m_desktop_watcher->fileChanged(uri); ++ } ++ m_pending_query_uris.clear(); ++ }); ++ } ++ m_pending_query_uris.insert(uri); ++ m_pending_query_timer->start(); ++} ++ ++void AdvancedDesktopItemModel::saveExtendItemInfo(int id) ++{ ++ //task#74174 销毁时保存扩展屏元素的坐标点 ++ for (int row = 0; row < rowCount(); row++) { ++ QModelIndex modelIndex = index(row, 0); ++ int currentId = modelIndex.data(AdvancedDesktopIconView::ScreenIdRole).toInt(); ++ if(currentId != id) { ++ continue; ++ } ++ //QMap<int, QVariant> roleData = itemData(modelIndex); ++ auto uri = modelIndex.data(AdvancedDesktopIconView::UriRole).toString(); ++ QVariant tmp = modelIndex.data(AdvancedDesktopIconView::ExceptedPositionRole); ++ QStringList extendPos = tmp.toStringList(); ++ if (3 != extendPos.count() && currentId != extendPos[2].toInt()) { ++ QPoint pos = modelIndex.data(AdvancedDesktopIconView::PositionRole).toPoint(); ++ QStringList topLeft; ++ topLeft<<QString::number(pos.x()); ++ topLeft<<QString::number(pos.y()); ++ topLeft<<QString::number(id); ++ extendPos = topLeft; ++ } ++ setData(modelIndex, extendPos, AdvancedDesktopIconView::ExtendScreenPositionRole); ++ QStringList singlesScreen = modelIndex.data(AdvancedDesktopIconView::SinglesSreenPositionRole).toStringList(); ++ if(2 == singlesScreen.count()) { ++ int col = extendPos.at(0).toInt(); ++ int row = extendPos.at(1).toInt(); ++ QPoint pos(col, row); ++ setData(modelIndex, pos, AdvancedDesktopIconView::PositionRole); ++ } ++ } ++} +diff --git a/peony-qt-desktop/advanced-desktop-item-model.h b/peony-qt-desktop/advanced-desktop-item-model.h +new file mode 100644 +index 0000000..2f09536 +--- /dev/null ++++ b/peony-qt-desktop/advanced-desktop-item-model.h +@@ -0,0 +1,204 @@ ++/* ++ * Peony-Qt ++ * ++ * Copyright (C) 2024, KylinSoft Co., Ltd. ++ * ++ * 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 3 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, see <https://www.gnu.org/licenses/>. ++ * ++ * Authors: yangyanwei <yangyanwei@kylinos.cn> ++ * ++ */ ++ ++#ifndef DESKTOPITEMMODEL_H ++#define DESKTOPITEMMODEL_H ++ ++#include <QAbstractListModel> ++#include <QStandardItemModel> ++#include <QQueue> ++#include <QPoint> ++#include <memory> ++#include <QStandardItem> ++#include <QSet> ++ ++#include "user-dir-manager.h" ++//#include "desktop-icon-view.h" ++class AdvancedDesktopIconView; ++namespace Peony { ++ ++class FileEnumerator; ++class FileInfo; ++class FileWatcher; ++class FileOperationInfo; ++ ++class AdvancedDesktopIconItem : public QStandardItem ++{ ++public: ++ explicit AdvancedDesktopIconItem(const QString &text); ++ explicit AdvancedDesktopIconItem(const QString &text, bool fileCreate); ++ void setData(const QVariant &value, int role = Qt::UserRole + 1) override; ++}; ++ ++struct IconData { ++ int screenId; ++ QPoint position; ++ QModelIndex modelIndex; ++ QString uri; ++ ++ bool operator<(const IconData &other) const { ++ if (screenId == other.screenId) { ++ if (position.x() == other.position.x()) { ++ return position.y() < other.position.y(); ++ } ++ return position.x() < other.position.x(); ++ } ++ return screenId < other.screenId; ++ } ++}; ++ ++class AdvancedDesktopItemModel : public QStandardItemModel ++{ ++ friend class AdvancedDesktopIconView; ++ Q_OBJECT ++public: ++ enum Role { ++ UriRole = Qt::UserRole, ++ IsLinkRole = Qt::UserRole + 1, ++ }; ++// Q_ENUM(Role) ++// enum DesktopIconRole { ++// UriRole = Qt::UserRole, ++// PositionRole = Qt::UserRole + 2, ++// ScreenIdRole = Qt::UserRole + 3, ++// ExceptedPositionRole = Qt::UserRole + 4, ++// SinglesSreenPositionRole = Qt::UserRole + 5, ++// ExtendScreenPositionRole = Qt::UserRole + 6, ++// IsFloatItemRole = Qt::UserRole + 7, ++// IsLinkRole = Qt::UserRole + 8 ++// }; Q_ENUM (DesktopIconRole) ++// enum DesktopIconRole { ++// UriRole = Qt::UserRole, ++// PositionRole = Qt::UserRole + 2, ++// ScreenIdRole = Qt::UserRole + 3, ++// ExceptedPositionRole = Qt::UserRole + 4, ++// SinglesSreenPositionRole = Qt::UserRole + 5, ++// ExtendScreenPositionRole = Qt::UserRole + 6, ++// IsFloatItemRole = Qt::UserRole + 7, ++// IsLinkRole = Qt::UserRole + 8 ++// }; Q_ENUM (DesktopIconRole) ++ explicit AdvancedDesktopItemModel(QObject *parent = nullptr); ++ ~AdvancedDesktopItemModel() override; ++ ++ const QModelIndex indexFromUri(const QString &uri); ++ // bool stringToPoint(QString &list, QPoint &pos, int &id); ++// const QString indexUri(const QModelIndex &index); ++ ++// // Basic functionality: ++// int rowCount(const QModelIndex &parent = QModelIndex()) const override; ++ ++ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; ++ ++// // Add data: ++// bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; ++// bool insertRow(int row, const QModelIndex &parent = QModelIndex()); ++ ++// // Remove data: ++// bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; ++// bool removeRow(int row, const QModelIndex &parent = QModelIndex()); ++ ++// Qt::ItemFlags flags(const QModelIndex &index) const override; ++ ++ QMimeData *mimeData(const QModelIndexList& indexes) const override; ++ ++ bool dropMimeData(const QMimeData *data, Qt::DropAction action, ++ int row, int column, const QModelIndex &parent) override; ++ ++// Qt::DropActions supportedDropActions() const override; ++// Qt::DropActions supportedDragActions() const override; ++ ++// bool acceptDropAction() const; ++// void setAcceptDropAction(bool acceptDropAction); ++//// Peony::DesktopIconView *getIconView(const QString &uri); ++// QPoint getFileMetaInfoPos(const QString &uri); ++// bool setData(const QModelIndex &index, const QVariant &value, ++// int role = Qt::EditRole) override; ++ ++ void updateMetaInfo(QStandardItem *item); ++ void getAllRestoreInfo(); ++ void saveExtendItemInfo(); ++ void resetExtendItemInfo(); ++ void clearExtendItemPos(bool saveId = false); ++ void saveExtendItemInfo(int id); ++ std::shared_ptr<FileInfo> getFileInfo(const QModelIndex& index) const; ++ ++Q_SIGNALS: ++// void requestLayoutNewItem(const QString &uri); ++ void requestClearIndexWidget(const QStringList &uris = QStringList()); ++// void requestUpdateItemPositions(const QString &uri = nullptr); ++ void refreshed(); ++ ++// // void fileCreated(const QString &uri); ++ ++ void prepareRefresh(); ++ void emitFinish(); ++ void sig_relayoutItems(); ++ void selectUri(const QString &uri); ++ ++public Q_SLOTS: ++ void refresh(); ++ ++protected Q_SLOTS: ++ void onEnumerateFinished(bool successed); ++ //void clearFloatItems(); ++ void fileCreated(const QString &uri); ++ void pendingQuery(const QString &uri); ++ ++private: ++ FileEnumerator *m_enumerator; ++ QList<std::shared_ptr<FileInfo>> m_files; ++ QList<std::shared_ptr<FileInfo>> m_querying_files; ++ std::shared_ptr<FileWatcher> m_trash_watcher; ++ std::shared_ptr<FileWatcher> m_desktop_watcher; ++ std::shared_ptr<FileWatcher> m_thumbnail_watcher; //just handle the thumbnail created. ++ ++// std::shared_ptr<FileWatcher> m_system_app_watcher; ++// std::shared_ptr<FileWatcher> m_andriod_app_watcher; ++ ++// QQueue<QString> m_new_file_info_query_queue; ++ ++ QStringList m_items_need_relayout; ++// QStringList m_destoryItems; ++ UserdirManager * m_dir_manager; ++ ++ std::shared_ptr<FileInfo> m_desktop_info; ++ std::shared_ptr<FileOperationInfo> m_renaming_operation_info; ++ ++ QPair<QString, QPoint> m_renaming_file_pos; ++ ++// bool m_accept_drop_action = true; ++ bool m_showFileExtension = true; ++// AdvancedDesktopIconView *view = nullptr; ++ QSet<QString> m_pending_query_uris; ++ QTimer *m_pending_query_timer = nullptr; ++ ++private: ++ void refreshInternal(); ++// /* \brief m_itemPosMap ++// * 当前视图中的文件uri和对应所在的网格位置 ++// */ ++// QHash<QString, QPoint> m_itemPosMap; ++}; ++ ++} ++ ++#endif // DESKTOPITEMMODEL_H +diff --git a/peony-qt-desktop/common.h b/peony-qt-desktop/common.h +new file mode 100644 +index 0000000..7dcbfea +--- /dev/null ++++ b/peony-qt-desktop/common.h +@@ -0,0 +1,40 @@ ++/* ++ * Peony-Qt ++ * ++ * Copyright (C) 2024, KylinSoft Co., Ltd. ++ * ++ * 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 3 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, see <https://www.gnu.org/licenses/>. ++ * ++ * Authors: yangyanwei <yangyanwei@kylinos.cn> ++ * ++ */ ++ ++#ifndef COMMON_H ++#define COMMON_H ++ ++#define PANEL_SETTINGS "org.ukui.panel.settings" ++#define UKUI_STYLE_SETTINGS "org.ukui.style" ++ ++#define SCREEN_ID "metadata::peony-qt-desktop-id" ++#define ITEM_POS_ATTRIBUTE "metadata::peony-qt-desktop-item-position" ++#define RESTORE_ITEM_POS_ATTRIBUTE "metadata::peony-qt-desktop-restore-item-position" ++#define RESTORE_EXTEND_ITEM_POS_ATTRIBUTE "metadata::peony-qt-desktop-restore-extend-item-position" ++#define RESTORE_SINGLESCREEN_ITEM_POS_ATTRIBUTE "metadata::peony-qt-desktop-restore-singlescreen-item-position" ++ ++#define ITEM_GRID_POS_ATTRIBUTE "metadata::peony-qt-desktop-item-grid-pos" ++#define RESTORE_ITEM_GRID_POS_ATTRIBUTE "metadata::peony-qt-desktop-restore-item-grid-position" ++#define RESTORE_EXTEND_ITEM_GRID_POS_ATTRIBUTE "metadata::peony-qt-desktop-restore-extend-item-grid-position" ++#define RESTORE_SINGLESCREEN_ITEM_GRID_POS_ATTRIBUTE "metadata::peony-qt-desktop-restore-singlescreen-item-grid-position" ++ ++#endif // COMMON_H +diff --git a/peony-qt-desktop/desktop-background-manager.cpp b/peony-qt-desktop/desktop-background-manager.cpp +index e599c38..6964f13 100644 +--- a/peony-qt-desktop/desktop-background-manager.cpp ++++ b/peony-qt-desktop/desktop-background-manager.cpp +@@ -33,6 +33,7 @@ + #include <QDBusReply> + #include <QFile> + #include <QProcess> ++#include <QTimer> + #include <global-settings.h> + + #include <gio/gio.h> +@@ -40,7 +41,9 @@ + #include <QDebug> + + static DesktopBackgroundManager *global_instance = nullptr; +-#define BACKGROUND_SETTINGS "org.mate.background" ++ ++#define BACKGROUND_MATE_SETTINGS "org.mate.background" ++#define BACKGROUND_SETTINGS "org.ukui.interface" + + DesktopBackgroundManager::DesktopBackgroundManager(QObject *parent) : QObject(parent) + { +@@ -61,6 +64,10 @@ DesktopBackgroundManager::DesktopBackgroundManager(QObject *parent) : QObject(pa + m_animation->start(); + } + updateScreens(); ++ QTimer::singleShot(200, this, [=](){ ++ m_animationRunning = false; ++ }); ++ + }); + + initGSettings(); +@@ -72,6 +79,13 @@ void DesktopBackgroundManager::initGSettings() + m_backgroundSettings = new QGSettings(BACKGROUND_SETTINGS, QByteArray(), this); + m_backgroundOption = m_backgroundSettings->get("pictureOptions").toString(); + ++// g_autoptr (GSettings) settings = g_settings_new_with_path("org.mate.background", "/org/mate/desktop/background/"); ++ g_autoptr (GSettings) settings = g_settings_new_with_path("org.ukui.interface", "/org/ukui/interface/"); ++ if (settings) { ++ bool writable = g_settings_is_writable(settings, "picture-filename"); ++ m_shouldSyncAccountBackground = writable; ++ } ++ } else if (QGSettings::isSchemaInstalled(BACKGROUND_MATE_SETTINGS)){ + g_autoptr (GSettings) settings = g_settings_new_with_path("org.mate.background", "/org/mate/desktop/background/"); + if (settings) { + bool writable = g_settings_is_writable(settings, "picture-filename"); +@@ -84,8 +98,10 @@ void DesktopBackgroundManager::initGSettings() + setBackground(); + if (m_backgroundSettings) { + connect(m_backgroundSettings, &QGSettings::changed, this, [=](const QString &key){ ++ m_animationRunning = true; + if (key == "pictureFilename") { + m_current_bg_path = m_backgroundSettings->get("pictureFilename").toString(); ++ m_shouldSyncAccountBackground = true; + setAccountBackground(); + } + if (key == "pictureFilename" || key == "primaryColor" || key == "pictureOptions") { +@@ -132,6 +148,16 @@ void DesktopBackgroundManager::setBackground() + } + + m_frontPixmap = QPixmap(defaultBg); ++ //ctyun项目反馈壁纸问题修复 ++ //fix jpeg file change suffix name to png, set as wallpaper fail issue ++ //fix bug#242528, can not read jpg wallpaper issue ++ if (m_frontPixmap.isNull()){ ++ QFile file(defaultBg); ++ if (file.open(QIODevice::ReadOnly)){ ++ m_frontPixmap.loadFromData(file.readAll()); ++ file.close(); ++ } ++ } + m_current_bg_path = defaultBg; + if (defaultBg != accountBack) + setAccountBackground(); +@@ -139,6 +165,51 @@ void DesktopBackgroundManager::setBackground() + m_animation->finished(); + } + ++/* ++* 1.为解决云桌面批量推送同名壁纸,重新设置壁纸不生效问题,增加强制更新接口; ++* 2.接口可以通过peony-qt-desktop -u 命令调用; ++* 3.接口目前只会使用gsettings设置的壁纸值做刷新,然后备份到个人壁纸数据; ++* 4.壁纸数据备份后,如果用户将壁纸文件本身删除,重新刷新也会使用备份的壁纸,不会丢失; ++*/ ++void DesktopBackgroundManager::forceUpdateBackground() ++{ ++ if (!m_backgroundSettings){ ++ qWarning() << "forceUpdateBackground failed, m_backgroundSettings not exist"; ++ return; ++ } ++ ++ m_backgroundOption = m_backgroundSettings->get("pictureOptions").toString(); ++ auto path = m_backgroundSettings->get("pictureFilename").toString(); ++ if (! QFile::exists(path)){ ++ qWarning() << "forceUpdateBackground failed, pictureFilename not exist"; ++ return; ++ } ++ ++ if (m_animation->state() == QVariantAnimation::Running) { ++ m_pendingPixmap = QPixmap(path); ++ m_current_bg_path = path; ++ } else { ++ m_frontPixmap = QPixmap(path); ++ //天翼云项目反馈壁纸问题修复 ++ //fix jpeg file change suffix name to png, set as wallpaper fail issue ++ if (m_frontPixmap.isNull()){ ++ QFile file(path); ++ if (file.open(QIODevice::ReadOnly)){ ++ m_frontPixmap.loadFromData(file.readAll()); ++ file.close(); ++ } ++ } ++ if (m_backPixmap.isNull()) { ++ m_backPixmap = m_frontPixmap; ++ } ++ m_current_bg_path = path; ++ m_animation->start(); ++ } ++ ++ updateScreens(); ++ setAccountBackground(); ++} ++ + QString DesktopBackgroundManager::getAccountBackground() + { + if (!m_shouldSyncAccountBackground) { +@@ -312,3 +383,8 @@ const QString &DesktopBackgroundManager::getBackgroundOption() + { + return m_backgroundOption; + } ++ ++bool DesktopBackgroundManager::AnimationRunning() ++{ ++ return m_animationRunning; ++} +diff --git a/peony-qt-desktop/desktop-background-manager.h b/peony-qt-desktop/desktop-background-manager.h +index 9117da8..0f242d4 100644 +--- a/peony-qt-desktop/desktop-background-manager.h ++++ b/peony-qt-desktop/desktop-background-manager.h +@@ -50,6 +50,9 @@ public: + bool getPaintBackground() const; + + const QString &getBackgroundOption(); ++ void forceUpdateBackground(); ++ ++ bool AnimationRunning(); + + Q_SIGNALS: + void screensUpdated(); +@@ -84,6 +87,7 @@ protected: + QString m_backgroundOption; + + bool m_shouldSyncAccountBackground = true; ++ bool m_animationRunning = false; + }; + + #endif // DESKTOPBACKGROUNDMANAGER_H +diff --git a/peony-qt-desktop/desktop-icon-view-delegate.cpp b/peony-qt-desktop/desktop-icon-view-delegate.cpp +index 5c781eb..d794b31 100644 +--- a/peony-qt-desktop/desktop-icon-view-delegate.cpp ++++ b/peony-qt-desktop/desktop-icon-view-delegate.cpp +@@ -21,8 +21,9 @@ + */ + + #include "desktop-icon-view-delegate.h" +-#include "desktop-icon-view.h" +- ++#include "advanced-desktop-icon-view.h" ++#include "desktop-item-proxy-model.h" ++#include "advanced-desktop-item-model.h" + #include "icon-view-editor.h" + + #include "file-operation-manager.h" +@@ -32,7 +33,7 @@ + + #include "icon-view-delegate.h" + #include "clipboard-utils.h" +-#include "desktop-item-model.h" ++ + #include "emblem-provider.h" + + #include <QPushButton> +@@ -65,7 +66,13 @@ DesktopIconViewDelegate::~DesktopIconViewDelegate() + void DesktopIconViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const + { + painter->save(); +- auto view = qobject_cast<Peony::DesktopIconView*>(parent()); ++ auto view = qobject_cast<AdvancedDesktopIconView*>(parent()); ++ ++ auto info = getFileInfo(index); ++ if (!info) { ++ painter->restore(); ++ return; ++ } + + auto style = option.widget->style(); + +@@ -75,7 +82,7 @@ void DesktopIconViewDelegate::paint(QPainter *painter, const QStyleOptionViewIte + opt.font = qApp->font(); + opt.fontMetrics = qApp->fontMetrics(); + +- if (view->state() == DesktopIconView::DraggingState) { ++ if (view->state() == AdvancedDesktopIconView::DraggingState) { + if (auto widget = view->indexWidget(index)) { + view->setIndexWidget(index, nullptr); + } +@@ -92,7 +99,7 @@ void DesktopIconViewDelegate::paint(QPainter *painter, const QStyleOptionViewIte + bool bCutFile = false; + auto clipedUris = ClipboardUtils::getInstance()->getCutFileUris(); + if (!clipedUris.isEmpty()){ +- if (clipedUris.contains(index.data(DesktopItemModel::UriRole).toString())) { ++ if (clipedUris.contains(index.data(AdvancedDesktopIconView::UriRole).toString())) { + painter->setOpacity(0.5); + bCutFile = true; + qDebug()<<"cut item in desktop"<<index.data(); +@@ -153,6 +160,9 @@ void DesktopIconViewDelegate::paint(QPainter *painter, const QStyleOptionViewIte + if((opt.state & QStyle::State_Enabled) && (opt.state & QStyle::State_Selected)) + { + opt.state &= ~QStyle::State_Selected; ++ opt.state &= ~QStyle::State_MouseOver; ++ } else if (opt.state.testFlag(QStyle::State_MouseOver)){ ++ opt.state &= ~QStyle::State_MouseOver; + } + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); +@@ -184,7 +194,7 @@ void DesktopIconViewDelegate::paint(QPainter *painter, const QStyleOptionViewIte + QPainter shadowPainter(&pixmap); + QColor shadow = Qt::black; + shadowPainter.setPen(shadow); +- IconViewTextHelper::paintText(&shadowPainter, opt, index, maxTextHight, 0, maxLineCount, false, shadow); ++ IconViewTextHelper::paintText(&shadowPainter, opt, index, maxTextHight, 0, maxLineCount, false, shadow, info); + shadowPainter.end(); + + QImage shadowImage(expectedSize + QSize(4, 4), QImage::Format_ARGB32_Premultiplied); +@@ -233,47 +243,57 @@ void DesktopIconViewDelegate::paint(QPainter *painter, const QStyleOptionViewIte + maxTextHight, + 0, + maxLineCount, +- false); ++ false, ++ Qt::transparent, ++ info); + painter->restore(); + + painter->restore(); + + QList<int> emblemPoses = {4, 3, 2, 1}; //bottom right, bottom left, top right, top left ++ QRect emblemRect = iconRect; ++ int emblemsSize = 16; ++ switch (view->zoomLevel()) { ++ case AdvancedDesktopIconView::Small: { ++ emblemsSize = 8; ++ break; ++ } ++ case AdvancedDesktopIconView::Normal: { ++ break; ++ } ++ case AdvancedDesktopIconView::Large: { ++ emblemsSize = 24; ++ break; ++ } ++ case AdvancedDesktopIconView::Huge: { ++ emblemsSize = 32; ++ break; ++ } ++ default: { ++ break; ++ } ++ } ++ auto rect = opt.rect; ++ int topLeftX = emblemRect.x()-emblemsSize/2+3 < rect.x()? rect.x() : emblemRect.x()-emblemsSize/2+3; ++ int topLeftY = emblemRect.y()-emblemsSize/2 < rect.y() - y_delta/2? rect.y() - y_delta/2 : emblemRect.y()-emblemsSize/2; ++ int bottomRightX = emblemRect.right()-emblemsSize/2 <= topLeftX + emblemsSize? topLeftX + emblemsSize + 5 : emblemRect.right()-emblemsSize/2 ; ++ int bottomRightY = emblemRect.bottom()-emblemsSize <= topLeftY + emblemsSize? topLeftY + emblemsSize + 5 : emblemRect.bottom()-emblemsSize; + + //paint link icon and locker icon +- FileInfo* file = FileInfo::fromUri(index.data(Qt::UserRole).toString()).get(); +- if ((index.data(Qt::UserRole).toString() != "computer:///") && (index.data(Qt::UserRole).toString() != "trash:///")) { +- QSize lockerIconSize = QSize(16, 16); +- int offset = 8; +- switch (view->zoomLevel()) { +- case DesktopIconView::Small: { +- lockerIconSize = QSize(8, 8); +- offset = 10; +- break; +- } +- case DesktopIconView::Normal: { +- break; +- } +- case DesktopIconView::Large: { +- offset = 4; +- lockerIconSize = QSize(24, 24); +- break; +- } +- case DesktopIconView::Huge: { +- offset = 2; +- lockerIconSize = QSize(32, 32); +- break; +- } +- default: { +- break; +- } +- } +- auto topRight = opt.rect.topRight(); +- topRight.setX(topRight.x() - opt.rect.width() + 10); +- topRight.setY(topRight.y() + 10); +- auto linkRect = QRect(topRight, lockerIconSize); +- +- if (! file->canRead()) ++ QString uri = index.data(AdvancedDesktopIconView::UriRole).toString(); ++ FileInfo* file = FileInfo::fromUri(uri).get(); ++ if ((uri != "computer:///") && (uri != "trash:///")) { ++ auto linkRect = QRect(QPoint(topLeftX, topLeftY), QSize(emblemsSize, emblemsSize)); ++ /** ++ * @bug #262561: [File Manager] PDF desktop shortcut files with deleted source files ++ * do not display the same icon on the desktop folder as on the desktop. ++ * ++ * If the source file of a symbolic link is deleted, an “X” icon will be displayed in the upper left corner. ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-09-11 ++ */ ++ if (! file->canRead() || !file->isExistTargetOfSymlink()) + { + emblemPoses.removeOne(1); + QIcon symbolicLinkIcon = QIcon::fromTheme("emblem-unreadable"); +@@ -296,40 +316,8 @@ void DesktopIconViewDelegate::paint(QPainter *painter, const QStyleOptionViewIte + + if (index.data(Qt::UserRole + 1).toBool()) { + emblemPoses.removeOne(3); +- QSize symbolicIconSize = QSize(16, 16); +- int offset = 8; +- switch (view->zoomLevel()) { +- case DesktopIconView::Small: { +- symbolicIconSize = QSize(8, 8); +- offset = 10; +- break; +- } +- case DesktopIconView::Normal: { +- break; +- } +- case DesktopIconView::Large: { +- offset = 4; +- symbolicIconSize = QSize(24, 24); +- break; +- } +- case DesktopIconView::Huge: { +- offset = 2; +- symbolicIconSize = QSize(32, 32); +- break; +- } +- default: { +- break; +- } +- } +-// auto topRight = opt.rect.topRight(); +-// topRight.setX(topRight.x() - offset - symbolicIconSize.width()); +-// topRight.setY(topRight.y() + offset); +-// auto linkRect = QRect(topRight, symbolicIconSize); + //Adjust link emblem to topLeft.link story#8354 +- auto topLeft = opt.rect.topLeft(); +- topLeft.setX(opt.rect.topLeft().x() + 10); +- topLeft.setY(opt.rect.topLeft().y() + offset + iconRect.height() - symbolicIconSize.height()); +- auto linkRect = QRect(topLeft, symbolicIconSize); ++ auto linkRect = QRect(QPoint(topLeftX, bottomRightY), QSize(emblemsSize, emblemsSize)); + QIcon symbolicLinkIcon = QIcon::fromTheme("emblem-link-symbolic"); + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); +@@ -346,71 +334,25 @@ void DesktopIconViewDelegate::paint(QPainter *painter, const QStyleOptionViewIte + + QIcon icon = QIcon::fromTheme(extensionsEmblem); + +- QSize emblemsIconSize = QSize(16, 16); +- int offset = 8; +- switch (view->zoomLevel()) { +- case DesktopIconView::Small: { +- emblemsIconSize = QSize(8, 8); +- offset = 10; +- break; +- } +- case DesktopIconView::Normal: { +- break; +- } +- case DesktopIconView::Large: { +- offset = 4; +- emblemsIconSize = QSize(24, 24); +- break; +- } +- case DesktopIconView::Huge: { +- offset = 2; +- emblemsIconSize = QSize(32, 32); +- break; +- } +- default: { +- break; +- } +- } +- + if (!icon.isNull()) { + int pos = emblemPoses.takeFirst(); + painter->save(); + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + switch (pos) { + case 1: { +- icon.paint(painter, +- opt.rect.topLeft().x() + 10, +- opt.rect.topLeft().y() + 10, +- emblemsIconSize.width(), +- emblemsIconSize.height(), +- Qt::AlignCenter); ++ icon.paint(painter, topLeftX, topLeftY, emblemsSize, emblemsSize, Qt::AlignCenter); + break; + } + case 2: { +- icon.paint(painter, +- opt.rect.topRight().x() - offset - emblemsIconSize.width(), +- opt.rect.topRight().y() + 10, +- emblemsIconSize.width(), +- emblemsIconSize.height(), +- Qt::AlignCenter); ++ icon.paint(painter, bottomRightX, topLeftY, emblemsSize, emblemsSize, Qt::AlignCenter); + break; + } + case 3: { +- icon.paint(painter, +- opt.rect.topLeft().x() + 10, +- opt.rect.topLeft().y() + offset + iconRect.height() - emblemsIconSize.height(), +- emblemsIconSize.width(), +- emblemsIconSize.height(), +- Qt::AlignCenter); ++ icon.paint(painter, topLeftX, bottomRightY, emblemsSize, emblemsSize, Qt::AlignCenter); + break; + } + case 4: { +- icon.paint(painter, +- opt.rect.topRight().x() - offset - emblemsIconSize.width(), +- opt.rect.topRight().y() + offset + iconRect.height() - emblemsIconSize.height(), +- emblemsIconSize.width(), +- emblemsIconSize.height(), +- Qt::AlignCenter); ++ icon.paint(painter, bottomRightX, bottomRightY, emblemsSize, emblemsSize, Qt::AlignCenter); + break; + } + default: +@@ -431,48 +373,17 @@ void DesktopIconViewDelegate::paint(QPainter *painter, const QStyleOptionViewIte + //return QStyledItemDelegate::paint(painter, option, index); + } + +-QSize DesktopIconViewDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +-{ +- QStyleOptionViewItem opt = option; +- initStyleOption(&opt, index); +- +- auto view = qobject_cast<DesktopIconView*>(this->parent()); +- auto iconSize = view->iconSize(); +- QFont font = view->font(); +- //font.setFamily(view->font().defaultFamily()); +- // asume max text size. +- font.setPointSize(15); +- auto fm = QFontMetrics(font); +- int width = iconSize.width() + 41; +- int height = iconSize.height() + fm.ascent()*2 + 20; +- return QSize(width, height); +-} +- + QWidget *DesktopIconViewDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const + { + auto edit = new IconViewEditor(parent); + auto font = option.font; +- auto view = qobject_cast<Peony::DesktopIconView*>(this->parent()); +-// switch (view->zoomLevel()) { +-// case DesktopIconView::Small: +-// font.setPixelSize(int(font.pixelSize() * 0.8)); +-// break; +-// case DesktopIconView::Large: +-// font.setPixelSize(int(font.pixelSize() * 1.2)); +-// break; +-// case DesktopIconView::Huge: +-// font.setPixelSize(int(font.pixelSize() * 1.4)); +-// break; +-// default: +-// break; +-// } ++ auto view = qobject_cast<AdvancedDesktopIconView*>(this->parent()); + + edit->setFont(font); + + edit->setContentsMargins(0, 0, 0, 0); + edit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +- edit->setMinimumSize(sizeHint(option, index).width(), 54); +- ++ edit->setMinimumSize(option.rect.width(), 54); + edit->blockSignals(true); + auto displayString = index.data(Qt::DisplayRole).toString(); + auto uri = index.data(Qt::UserRole).toString(); +@@ -504,7 +415,7 @@ QWidget *DesktopIconViewDelegate::createEditor(QWidget *parent, const QStyleOpti + auto editDestroyConn = connect(edit, &IconViewEditor::destroyed, getView(), [=](){ + getView()->setEditFlag(false); + }); +- connect(getView(), &DesktopIconView::destroyed, edit, [=](){ ++ connect(getView(), &AdvancedDesktopIconView::destroyed, edit, [=](){ + disconnect(editDestroyConn); + }); + +@@ -551,7 +462,7 @@ void DesktopIconViewDelegate::updateEditorGeometry(QWidget *editor, const QStyle + //edit->move(opt.rect.x(), opt.rect.y() + y_delta + 10); + edit->move(opt.rect.x(), opt.rect.y() + iconExpectedSize.height() + 5); + +- edit->resize(edit->document()->size().width(), edit->document()->size().height() + 10); ++ edit->resize(edit->size().width(), edit->document()->size().height() + 10); + } + + void DesktopIconViewDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const +@@ -588,7 +499,7 @@ void DesktopIconViewDelegate::setModelData(QWidget *editor, QAbstractItemModel * + fileOpMgr->startOperation(renameOp, true); + } else { + auto fileOpMgr = FileOperationManager::getInstance(); +- auto renameOp = new FileRenameOperation(index.data(Qt::UserRole).toString(), newName); ++ auto renameOp = new FileRenameOperation(index.data(AdvancedDesktopIconView::UriRole).toString(), newName); + getView()->setRenaming(true); + + //select file when rename finished +@@ -625,9 +536,9 @@ void DesktopIconViewDelegate::slot_finishEdit() + getView()->setEditFlag(false); + } + +-DesktopIconView *DesktopIconViewDelegate::getView() const ++AdvancedDesktopIconView *DesktopIconViewDelegate::getView() const + { +- auto view = qobject_cast<Peony::DesktopIconView*>(parent()); ++ auto view = qobject_cast<AdvancedDesktopIconView*>(parent()); + return view; + } + +@@ -635,3 +546,35 @@ void DesktopIconViewDelegate::initIndexOption(QStyleOptionViewItem *option, cons + { + return initStyleOption(option, index); + } ++ ++std::shared_ptr<FileInfo> DesktopIconViewDelegate::getFileInfo(const QModelIndex &index) const ++{ ++ // Get the associated view ++ auto view = getView(); ++ if (!view) { ++ return nullptr; ++ } ++ ++ // Cast the model to DesktopItemProxyModel ++ auto proxyModel = qobject_cast<Peony::DesktopItemProxyModel*>(view->model()); ++ if (!proxyModel) { ++ return nullptr; ++ } ++ ++ // Get the source model (AdvancedDesktopItemModel) ++ auto originalModel = qobject_cast<Peony::AdvancedDesktopItemModel*>(proxyModel->sourceModel()); ++ if (!originalModel) { ++ return nullptr; ++ } ++ ++ // Map the proxy index to the source index ++ QModelIndex sourceIndex = proxyModel->mapToSource(index); ++ ++ // Retrieve and return the FileInfo from the original model ++ auto info = originalModel->getFileInfo(sourceIndex); ++ if (!info) { ++ return nullptr; ++ } ++ ++ return info; ++} +diff --git a/peony-qt-desktop/desktop-icon-view-delegate.h b/peony-qt-desktop/desktop-icon-view-delegate.h +index d497f2a..2806156 100644 +--- a/peony-qt-desktop/desktop-icon-view-delegate.h ++++ b/peony-qt-desktop/desktop-icon-view-delegate.h +@@ -24,12 +24,12 @@ + #define DESKTOPICONVIEWDELEGATE_H + + #include <QStyledItemDelegate> ++#include <memory> + + class QPushButton; +- ++class AdvancedDesktopIconView; + namespace Peony { +- +-class DesktopIconView; ++class FileInfo; + + class DesktopIconViewDelegate : public QStyledItemDelegate + { +@@ -41,13 +41,20 @@ public: + void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override { + return QStyledItemDelegate::initStyleOption(option, index); + } +- QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; + +- DesktopIconView *getView() const; ++ AdvancedDesktopIconView *getView() const; + + //初始化option + void initIndexOption(QStyleOptionViewItem *option, + const QModelIndex &index) const; ++ ++ /** ++ * @brief Retrieves the FileInfo object associated with a given model index ++ * @param index The QModelIndex for which to retrieve the FileInfo ++ * @return std::shared_ptr<FileInfo> The FileInfo object, or nullptr if not found ++ */ ++ std::shared_ptr<FileInfo> getFileInfo(const QModelIndex &index) const; ++ + protected: + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + +diff --git a/peony-qt-desktop/desktop-icon-view.cpp b/peony-qt-desktop/desktop-icon-view.cpp +old mode 100755 +new mode 100644 +index e9f0789..4cd5d33 +--- a/peony-qt-desktop/desktop-icon-view.cpp ++++ b/peony-qt-desktop/desktop-icon-view.cpp +@@ -44,6 +44,7 @@ + #include "file-item-model.h" + #include "file-info-job.h" + #include "file-launch-manager.h" ++#include "file-launch-action.h" + #include <QProcess> + + #include <QDesktopServices> +@@ -288,6 +289,14 @@ DesktopIconView::DesktopIconView(QWidget *parent) : QListView(parent) + return; + }); + ++ connect(m_model, &DesktopItemModel::refreshFilter, this, [=]() { ++ m_proxy_model->setShowHidden(GlobalSettings::getInstance()->getValue(SHOW_HIDDEN_PREFERENCE).toBool()); ++ QTimer::singleShot(100, this, [=]() { ++ resetAllItemPositionInfos(); ++ refresh(); ++ }); ++ }); ++ + connect(m_model, &DesktopItemModel::requestClearIndexWidget, this, &DesktopIconView::clearAllIndexWidgets); + + connect(m_proxy_model, &QSortFilterProxyModel::layoutChanged, this, [=]() { +@@ -1038,6 +1047,7 @@ void DesktopIconView::openFileByUri(QString uri) + return; + } + ++#ifdef USE_QPROCESS_LAUNCH_DIR + #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + QProcess p; + QUrl url = uri; +@@ -1073,6 +1083,11 @@ void DesktopIconView::openFileByUri(QString uri) + } + + p.startDetached("/usr/bin/peony", QStringList()<<strq<<"%U&"); ++#endif ++#else ++ auto action = Peony::FileLaunchManager::getPeonyAction(uri); ++ action->lauchFileAsync(); ++ action->deleteLater(); + #endif + } else { + if (!(info->isDesktopFile() && execSharedFileLink(uri))) { +@@ -1530,9 +1545,25 @@ void DesktopIconView::keyPressEvent(QKeyEvent *e) + case Qt::Key_Return: + { + auto selections = this->getSelections(); +- for (auto uri : selections) ++ if (selections.count() >= 1) + { +- openFileByUri(uri); ++ QStringList files; ++ QStringList dirs; ++ for (auto uri : selections) { ++ auto info = Peony::FileInfo::fromUri(uri); ++ if (info->isDir() || info->isVolume()) { ++ dirs<<uri; ++ } else { ++ files<<uri; ++ } ++ } ++ for (auto uri : dirs) { ++ openFileByUri(uri); ++ } ++ ++ if(!files.isEmpty()) { ++ Peony::FileLaunchManager::openFilesByDefaultApplications(files); ++ } + } + } + break; +@@ -2091,6 +2122,14 @@ void DesktopIconView::mouseMoveEvent(QMouseEvent *e) + } + } + ++ // fix #220390 ++ if (state() == QListView::DragSelectingState) { ++ auto rubberbandRect = QRect(m_press_pos, e->pos()); ++ rubberbandRect = rubberbandRect.normalized(); ++ rubberbandRect.adjust(-5, -5, 5, 5); ++ viewport()->update(rubberbandRect); ++ } ++ + QListView::mouseMoveEvent(e); + } + +diff --git a/peony-qt-desktop/desktop-icon-view.h b/peony-qt-desktop/desktop-icon-view.h +old mode 100755 +new mode 100644 +diff --git a/peony-qt-desktop/desktop-index-widget.cpp b/peony-qt-desktop/desktop-index-widget.cpp +index 89c81c0..21201f6 100644 +--- a/peony-qt-desktop/desktop-index-widget.cpp ++++ b/peony-qt-desktop/desktop-index-widget.cpp +@@ -24,9 +24,10 @@ + #include "desktop-index-widget.h" + + #include "desktop-icon-view-delegate.h" +-#include "desktop-icon-view.h" ++#include "advanced-desktop-icon-view.h" + #include "file-info.h" + #include "emblem-provider.h" ++#include "global-settings.h" + + #include <QPainter> + #include <QStyle> +@@ -59,11 +60,11 @@ DesktopIndexWidget::DesktopIndexWidget(DesktopIconViewDelegate *delegate, + updateItem(); + + //FIXME: how to handle it in old version? +-#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) +- connect(qApp, &QApplication::fontChanged, this, [=]() { +- m_delegate->getView()->setIndexWidget(m_index, nullptr); +- }); +-#endif ++//#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) ++// connect(qApp, &QApplication::fontChanged, this, [=]() { ++// m_delegate->getView()->setIndexWidget(m_index, nullptr); ++// }); ++//#endif + + auto view = m_delegate->getView(); + view->m_real_do_edit = false; +@@ -197,41 +198,48 @@ void DesktopIndexWidget::paintEvent(QPaintEvent *e) + p.restore(); + + QList<int> emblemPoses = {4, 3, 2, 1}; //bottom right, bottom left, top right, top left ++ QRect emblemRect = iconRect; ++ int emblemsSize = 16; ++ switch (view->zoomLevel()) { ++ case AdvancedDesktopIconView::Small: { ++ emblemsSize = 8; ++ break; ++ } ++ case AdvancedDesktopIconView::Normal: { ++ break; ++ } ++ case AdvancedDesktopIconView::Large: { ++ emblemsSize = 24; ++ break; ++ } ++ case AdvancedDesktopIconView::Huge: { ++ emblemsSize = 32; ++ break; ++ } ++ default: { ++ break; ++ } ++ } ++ auto rect = opt.rect; ++ int topLeftX = emblemRect.x()-emblemsSize/2+3 < rect.x()? rect.x() : emblemRect.x()-emblemsSize/2+3; ++ int topLeftY = emblemRect.y()-emblemsSize/2 < rect.y() - y_delta/2? rect.y() - y_delta/2 : emblemRect.y()-emblemsSize/2; ++ int bottomRightX = emblemRect.right()-emblemsSize/2 <= topLeftX + emblemsSize? topLeftX + emblemsSize + 5 : emblemRect.right()-emblemsSize/2 ; ++ int bottomRightY = emblemRect.bottom()-emblemsSize <= topLeftY + emblemsSize? topLeftY + emblemsSize + 5 : emblemRect.bottom()-emblemsSize; + + //paint link icon and locker icon + FileInfo *file = FileInfo::fromUri(m_index.data(Qt::UserRole).toString()).get(); + if ((m_index.data(Qt::UserRole).toString() != "computer:///") && (m_index.data(Qt::UserRole).toString() != "trash:///")) { +- QSize lockerIconSize = QSize(16, 16); +- int offset = 8; +- switch (view->zoomLevel()) { +- case DesktopIconView::Small: { +- lockerIconSize = QSize(8, 8); +- offset = 10; +- break; +- } +- case DesktopIconView::Normal: { +- break; +- } +- case DesktopIconView::Large: { +- offset = 4; +- lockerIconSize = QSize(24, 24); +- break; +- } +- case DesktopIconView::Huge: { +- offset = 2; +- lockerIconSize = QSize(32, 32); +- break; +- } +- default: { +- break; +- } +- } +- auto topRight = opt.rect.topRight(); +- topRight.setX(topRight.x() - opt.rect.width() + 10); +- topRight.setY(topRight.y() + 10); +- auto linkRect = QRect(topRight, lockerIconSize); +- +- if (! file->canRead()) ++ auto linkRect = QRect(QPoint(topLeftX, topLeftY), QSize(emblemsSize, emblemsSize)); ++ /** ++ * @bug #262561: [File Manager] PDF desktop shortcut files with deleted source files ++ * do not display the same icon on the desktop folder as on the desktop. ++ * ++ * If the source file of a symbolic link is deleted, an “X” icon will be displayed in the upper left corner. ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-09-11 ++ */ ++ if (! file->canRead() || !file->isExistTargetOfSymlink()) + { + emblemPoses.removeOne(1); + QIcon symbolicLinkIcon = QIcon::fromTheme("emblem-unreadable"); +@@ -254,37 +262,7 @@ void DesktopIndexWidget::paintEvent(QPaintEvent *e) + + if (m_index.data(Qt::UserRole + 1).toBool()) { + emblemPoses.removeOne(3); +- QSize symbolicIconSize = QSize(16, 16); +- int offset = 8; +- switch (view->zoomLevel()) { +- case DesktopIconView::Small: { +- symbolicIconSize = QSize(8, 8); +- offset = 10; +- break; +- } +- case DesktopIconView::Normal: { +- break; +- } +- case DesktopIconView::Large: { +- offset = 4; +- symbolicIconSize = QSize(24, 24); +- break; +- } +- case DesktopIconView::Huge: { +- offset = 2; +- symbolicIconSize = QSize(32, 32); +- break; +- } +- default: { +- break; +- } +- } +- +- //Adjust link emblem to topLeft.link story#8354 +- auto topLeft = opt.rect.topLeft(); +- topLeft.setX(opt.rect.topLeft().x() + 10); +- topLeft.setY(opt.rect.topLeft().y() + offset + iconRect.height() - symbolicIconSize.height()); +- auto linkRect = QRect(topLeft, symbolicIconSize); ++ auto linkRect = QRect(QPoint(topLeftX, bottomRightY), QSize(emblemsSize, emblemsSize)); + QIcon symbolicLinkIcon = QIcon::fromTheme("emblem-link-symbolic"); + p.save(); + p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); +@@ -301,71 +279,25 @@ void DesktopIndexWidget::paintEvent(QPaintEvent *e) + + QIcon icon = QIcon::fromTheme(extensionsEmblem); + +- QSize emblemsIconSize = QSize(16, 16); +- int offset = 8; +- switch (view->zoomLevel()) { +- case DesktopIconView::Small: { +- emblemsIconSize = QSize(8, 8); +- offset = 10; +- break; +- } +- case DesktopIconView::Normal: { +- break; +- } +- case DesktopIconView::Large: { +- offset = 4; +- emblemsIconSize = QSize(24, 24); +- break; +- } +- case DesktopIconView::Huge: { +- offset = 2; +- emblemsIconSize = QSize(32, 32); +- break; +- } +- default: { +- break; +- } +- } +- + if (!icon.isNull()) { + int pos = emblemPoses.takeFirst(); + p.save(); + p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + switch (pos) { + case 1: { +- icon.paint(&p, +- opt.rect.topLeft().x() + 10, +- opt.rect.topLeft().y() + 10, +- emblemsIconSize.width(), +- emblemsIconSize.height(), +- Qt::AlignCenter); ++ icon.paint(&p, topLeftX, topLeftY, emblemsSize, emblemsSize, Qt::AlignCenter); + break; + } + case 2: { +- icon.paint(&p, +- opt.rect.topRight().x() - offset - emblemsIconSize.width(), +- opt.rect.topRight().y() + 10, +- emblemsIconSize.width(), +- emblemsIconSize.height(), +- Qt::AlignCenter); ++ icon.paint(&p, bottomRightX, topLeftY, emblemsSize, emblemsSize, Qt::AlignCenter); + break; + } + case 3: { +- icon.paint(&p, +- opt.rect.topLeft().x() + 10, +- opt.rect.topLeft().y() + offset + iconRect.height() - emblemsIconSize.height(), +- emblemsIconSize.width(), +- emblemsIconSize.height(), +- Qt::AlignCenter); ++ icon.paint(&p, topLeftX, bottomRightY, emblemsSize, emblemsSize, Qt::AlignCenter); + break; + } + case 4: { +- icon.paint(&p, +- opt.rect.topRight().x() - offset - emblemsIconSize.width(), +- opt.rect.topRight().y() + offset + iconRect.height() - emblemsIconSize.height(), +- emblemsIconSize.width(), +- emblemsIconSize.height(), +- Qt::AlignCenter); ++ icon.paint(&p, bottomRightX, bottomRightY, emblemsSize, emblemsSize, Qt::AlignCenter); + break; + } + default: +@@ -442,6 +374,21 @@ void DesktopIndexWidget::mousePressEvent(QMouseEvent *event) + + void DesktopIndexWidget::mouseDoubleClickEvent(QMouseEvent *event) + { ++ if (! GlobalSettings::getInstance()->getValue(ENABLE_DOUBLE_CLICK_DESKTOP).toBool()) ++ return; ++ ++ /** ++ * @bug #250731: [File Manager] Right clicking on the same folder several times in the file manager will take you to the folder ++ * ++ * Prevent double-click events from triggering on right-click ++ * Only double left clicks will be processed ++ * ++ * @author Renyg ++ * @date 2024-08-13 ++ */ ++ if (event->button() == Qt::RightButton) { ++ return; ++ } + auto view = m_delegate->getView(); + if (!view->selectionModel()->selectedIndexes().contains(m_index)) { + view->m_real_do_edit = false; +@@ -488,8 +435,8 @@ void DesktopIndexWidget::updateItem() + // } + + // qDebug() << "updateItem fixedHeight:" <<fixedHeight <<rawHeight <<m_option.text; +- if (fixedHeight < rawHeight) +- fixedHeight = rawHeight; ++// if (fixedHeight < rawHeight) ++// fixedHeight = rawHeight; + + m_option.text = m_index.data().toString(); + //qDebug()<<m_option.text; +@@ -516,4 +463,5 @@ void DesktopIndexWidget::updateItem() + fixedHeight = qMin(view->viewport()->height() - this->geometry().y(), fixedHeight); + + setFixedHeight(fixedHeight); ++ m_option.rect.setHeight(fixedHeight); + } +diff --git a/peony-qt-desktop/desktop-item-model.cpp b/peony-qt-desktop/desktop-item-model.cpp +index 556e1fb..aa4fd5b 100644 +--- a/peony-qt-desktop/desktop-item-model.cpp ++++ b/peony-qt-desktop/desktop-item-model.cpp +@@ -48,6 +48,7 @@ + #include "desktop-icon-view-delegate.h" + #include "desktop-menu-plugin-manager.h" + #include "emblem-provider.h" ++#include "tooltips-manager.h" + + #include <QStandardPaths> + #include <QIcon> +@@ -363,7 +364,7 @@ DesktopItemModel::DesktopItemModel(QObject *parent) + ThumbnailManager::getInstance()->createThumbnail(uri, m_thumbnail_watcher, true); + this->dataChanged(indexFromUri(uri), indexFromUri(uri)); + Q_EMIT this->requestClearIndexWidget(QStringList()<<uri); +- ++ refreshFilter(); + }); + job->queryAsync(); + this->dataChanged(indexFromUri(uri), indexFromUri(uri)); +@@ -628,30 +629,17 @@ QVariant DesktopItemModel::data(const QModelIndex &index, int role) const + return QVariant(displayName); + } + case Qt::ToolTipRole: { +-// // fix #80257 +-// switch (index.row()) { +-// case 0: +-// return tr("Computer"); +-// case 1: +-// return tr("Trash"); +-// default: +-// break; +-// } +- +- //fix bug#53504, desktop files not show same name issue +- if (info->isDesktopFile()) +- { +- auto displayName = FileUtils::handleDesktopFileName(info->uri(), info->displayName()); +- return displayName; +- } +- return info->displayName(); ++ /** ++ * @task #346285: 【Tooltips specification】Change the desktop icon (including the management-desktop-application icon) tooltips display, ++ * add supplementary text ++ * ++ * @author: Renyg <renyangguang@kylinos.cn> ++ * @date: 2024-09-25 ++ */ ++ return QVariant(TooltipsManagerInstance.generateTooltip(info.get())); + } + case Qt::DecorationRole: { +- auto thumbnail = ThumbnailManager::getInstance()->tryGetThumbnail(info->uri()); +- if (!thumbnail.isNull()) { +- return thumbnail; +- } +- return QIcon::fromTheme(info->iconName(), QIcon::fromTheme("unknown")); ++ return info->getIcon(); + } + case UriRole: + return info->uri(); +@@ -661,6 +649,14 @@ QVariant DesktopItemModel::data(const QModelIndex &index, int role) const + return QVariant(); + } + ++std::shared_ptr<FileInfo> DesktopItemModel::getFileInfo(const QModelIndex &index) const ++{ ++ if (!index.isValid() || m_files.isEmpty() || index.row() >= m_files.length()) ++ return nullptr; ++ ++ return m_files.at(index.row()); ++} ++ + void DesktopItemModel::onEnumerateFinished(bool successed) + { + if (!successed) { +diff --git a/peony-qt-desktop/desktop-item-model.h b/peony-qt-desktop/desktop-item-model.h +index 4684741..948d4ee 100644 +--- a/peony-qt-desktop/desktop-item-model.h ++++ b/peony-qt-desktop/desktop-item-model.h +@@ -80,12 +80,14 @@ public: + bool acceptDropAction() const; + void setAcceptDropAction(bool acceptDropAction); + Peony::DesktopIconView *getIconView(const QString &uri); ++ std::shared_ptr<FileInfo> getFileInfo(const QModelIndex& index) const; + + Q_SIGNALS: + void requestLayoutNewItem(const QString &uri); + void requestClearIndexWidget(const QStringList &uris = QStringList()); + void requestUpdateItemPositions(const QString &uri = nullptr); + void refreshed(); ++ void refreshFilter(); + + // void fileCreated(const QString &uri); + +diff --git a/peony-qt-desktop/desktop-item-proxy-model.cpp b/peony-qt-desktop/desktop-item-proxy-model.cpp +index 1f35648..75d7e3f 100644 +--- a/peony-qt-desktop/desktop-item-proxy-model.cpp ++++ b/peony-qt-desktop/desktop-item-proxy-model.cpp +@@ -68,6 +68,9 @@ DesktopItemProxyModel::DesktopItemProxyModel(QObject *parent) : QSortFilterProxy + }); + //qDebug() <<"DesktopItemProxyModel:" <<settings->isExist(SHOW_HIDDEN_PREFERENCE)<<m_show_hidden; + ++ m_sort_type = settings->getValue(LAST_DESKTOP_SORT_ORDER).toInt(); ++ m_sort_order = settings->getValue(DESKTOP_SORT_ORDER).toInt(); ++ + m_bwListInfo = new BWListInfo(); + m_jsonOp = new PeonyJsonOperation(); + QString jsonPath=QDir::homePath()+"/.config/peony-security-config.json"; +@@ -92,28 +95,21 @@ bool DesktopItemProxyModel::filterAcceptsRow(int source_row, const QModelIndex & + auto uri = sourceIndex.data(Qt::UserRole).toString(); + auto info = FileInfo::fromUri(uri); + +- /* task#63345 通过.hidden文件来设置隐藏文件和目录 */ +- bool isHidden = info->property(G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN).toBool(); +- //qDebug()<<"File view .hidden file hidden,uri:"<<uri<<" isHidden:"<<isHidden; +- if(isHidden && !info->displayName().startsWith(".")){/* .xxx文件遵循是否显示隐藏文件的逻辑 */ +- return false; +- }//end +- + //qDebug()<<"fiter"<<uri<<info->displayName(); + if (info->displayName().isNull()) { + //return false; + } +- if (! m_show_hidden && info->displayName().startsWith(".")) { ++ if (! m_show_hidden && info->isHiddenFile()) { + return false; + } + //task#74174 通过id筛选出需要在该view中显示项 +- auto metaInfo = FileMetaInfo::fromUri(uri); +- if (metaInfo) { +- int id = metaInfo->getMetaInfoInt("peony-qt-desktop-id"); +- if (id != m_id) { +- return false; +- } ++ int id = sourceIndex.data(Qt::UserRole+3).toInt(); ++ QPoint pos = sourceIndex.data(Qt::UserRole + 2).toPoint(); ++ ++ if (m_id != id) { ++ return false; + } ++ + //fix desktop show Desktop folder issue, bug#20293 + if (QUrl(uri).path() == QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) + "/Desktop" + || QUrl(uri).path() == QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/Desktop" + "/Desktop") +@@ -219,21 +215,7 @@ bool DesktopItemProxyModel::lessThan(const QModelIndex &source_left, const QMode + if (FileOperationUtils::leftNameIsDuplicatedFileOfRightName(leftInfo->displayName(), rightInfo->displayName())) { + return FileOperationUtils::leftNameLesserThanRightName(leftInfo->displayName(), rightInfo->displayName()); + } +- if (startWithChinese(leftInfo->displayName())) { +- if (!startWithChinese(rightInfo->displayName())) { +- return (sortOrder()==Qt::AscendingOrder)? true: false; +- } else { +- //chinese pinyin sort order is reversed compared with english. +- //return !QSortFilterProxyModel::lessThan(source_left, source_right); +- //fix bug#89115, chinese files not sort by name pinyin +- return comparer.compare(leftInfo->displayName(), rightInfo->displayName()) > 0; +- } +- } else { +- if (startWithChinese(rightInfo->displayName())) { +- return (sortOrder()==Qt::AscendingOrder)? false: true; +- } +- } +- return comparer.compare(leftInfo->displayName(), rightInfo->displayName()) > 0; ++ goto default_sort; + } + case ModifiedDate: { + if (leftInfo->modifiedTime() == rightInfo->modifiedTime()) +@@ -241,9 +223,9 @@ bool DesktopItemProxyModel::lessThan(const QModelIndex &source_left, const QMode + return leftInfo->modifiedTime() > rightInfo->modifiedTime(); + } + case FileType: { +- if (leftInfo->type() == rightInfo->type()) ++ if (leftInfo->fileType() == rightInfo->fileType()) + goto default_sort; +- return leftInfo->type() > rightInfo->type(); ++ return leftInfo->fileType() > rightInfo->fileType(); + } + case FileSize: { + if (leftInfo->size() == rightInfo->size()) +@@ -258,12 +240,21 @@ default_sort: + QString leftDisplayName = leftInfo->displayName(); + QString rightDisplayName = rightInfo->displayName(); + +- if(startWithChinese(leftDisplayName) && ! startWithChinese(rightDisplayName)) +- return true; +- else if(! startWithChinese(leftDisplayName) && startWithChinese(rightDisplayName)) +- return false; +- else ++ if (startWithChinese(leftInfo->displayName())) { ++ if (!startWithChinese(rightInfo->displayName())) { ++ return (sortOrder()==Qt::AscendingOrder)? true: false; ++ } else { ++ //chinese pinyin sort order is reversed compared with english. ++ //return !QSortFilterProxyModel::lessThan(source_left, source_right); ++ //fix bug#89115, chinese files not sort by name pinyin ++ return comparer.compare(leftInfo->displayName(), rightInfo->displayName()) > 0; ++ } ++ } else { ++ if (startWithChinese(rightInfo->displayName())) { ++ return (sortOrder()==Qt::AscendingOrder)? false: true; ++ } + return comparer.compare(leftDisplayName, rightDisplayName) > 0; ++ } + + return QSortFilterProxyModel::lessThan(source_left, source_right); + } +@@ -286,12 +277,12 @@ int DesktopItemProxyModel::updateBlackAndWriteLists() + + void DesktopItemProxyModel::invalidateModel() + { +- invalidateFilter(); ++ invalidateFilter(); + } + + void DesktopItemProxyModel::setId(int id) + { +- m_id = id; ++ m_id = id; + } + + QString DesktopItemProxyModel::getBlackAndWhiteModel() +@@ -308,3 +299,48 @@ QSet<QString> DesktopItemProxyModel::getBWListInfo() + { + return m_bwListInfo->getBWListInfo(); + } ++ ++void DesktopItemProxyModel::setDesktopUseAutoLayout(bool desktopUseAutoLayout) ++{ ++ m_desktopUseAutoLayout = desktopUseAutoLayout; ++} ++ ++bool DesktopItemProxyModel::getDesktopUseAutoLayout() const ++{ ++ return m_desktopUseAutoLayout; ++} ++ ++QStringList DesktopItemProxyModel::getSortedUris() const ++{ ++ return m_sortedUris; ++} ++ ++void DesktopItemProxyModel::setSortedUris(const QStringList &sortedUris) ++{ ++ m_sortedUris = sortedUris; ++// Q_EMIT requestSyncSortedUrisWithDelay(); ++} ++ ++QModelIndexList DesktopItemProxyModel::getAllFileIndexes() ++{ ++ QModelIndexList l; ++ int i = 0; ++ while (this->index(i, 0, QModelIndex()).isValid()) { ++ auto index = this->index(i, 0, QModelIndex()); ++ if (m_show_hidden) { ++ l<<index; ++ } else { ++ auto disyplayName = index.data(Qt::DisplayRole).toString(); ++ if (disyplayName.isEmpty()) { ++ auto uri = this->index(i, 0, QModelIndex()).data(Qt::UserRole).toString(); ++ disyplayName = FileUtils::getFileDisplayName(uri); ++ } ++ if (!disyplayName.startsWith(".")) { ++ l<<index; ++ } ++ } ++ ++ i++; ++ } ++ return l; ++} +diff --git a/peony-qt-desktop/desktop-item-proxy-model.h b/peony-qt-desktop/desktop-item-proxy-model.h +index 6b30770..dbc86f5 100644 +--- a/peony-qt-desktop/desktop-item-proxy-model.h ++++ b/peony-qt-desktop/desktop-item-proxy-model.h +@@ -50,6 +50,14 @@ public: + return m_sort_type; + } + ++ void setSortOrder(int sortOrder) { ++ m_sort_order = sortOrder; ++ } ++ ++ int getSortOrder() { ++ return m_sort_order; ++ } ++ + void setShowHidden(bool showHidden); + int updateBlackAndWriteLists(); + QString getBlackAndWhiteModel(); +@@ -63,16 +71,28 @@ public: + void invalidateModel(); + void setId(int id); + ++ QModelIndexList getAllFileIndexes(); ++ ++ void setDesktopUseAutoLayout(bool desktopUseAutoLayout); ++ bool getDesktopUseAutoLayout() const; ++ ++ QStringList getSortedUris() const; ++ void setSortedUris(const QStringList &sortedUris); ++ + Q_SIGNALS: + void showHiddenFile(); ++ void requestSyncSortedUrisWithDelay(int msec = 1000); + + private: + int m_sort_type = Other; + bool m_show_hidden; +- ++ int m_sort_order = 0; + BWListInfo *m_bwListInfo; + PeonyJsonOperation *m_jsonOp; + int m_id = 0; ++ ++ bool m_desktopUseAutoLayout = false; ++ QStringList m_sortedUris; + }; + + } +diff --git a/peony-qt-desktop/desktop-menu-plugin-manager.cpp b/peony-qt-desktop/desktop-menu-plugin-manager.cpp +index ae1770c..d5d8ae7 100644 +--- a/peony-qt-desktop/desktop-menu-plugin-manager.cpp ++++ b/peony-qt-desktop/desktop-menu-plugin-manager.cpp +@@ -36,6 +36,8 @@ + #include "vfs-plugin-iface.h" + #include "vfs-plugin-manager.h" + #include "emblem-plugin-iface.h" ++#include "properties-window-tab-page-plugin-iface.h" ++#include "properties-window.h" + #include "file-watcher.h" + + using namespace Peony; +@@ -73,6 +75,9 @@ void DesktopMenuPluginManager::loadAsync() + if ("libpeony-filesafe-menu-plugin.so" == fileName) + continue; + QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName)); ++ if (fileName == "libsafe-context-menu.so") { ++ pluginLoader.setLoadHints(pluginLoader.loadHints() | QLibrary::DeepBindHint); ++ } + qDebug()<<pluginLoader.fileName(); + qDebug()<<pluginLoader.metaData(); + qDebug()<<pluginLoader.load(); +diff --git a/peony-qt-desktop/desktop-menu.cpp b/peony-qt-desktop/desktop-menu.cpp +index def6d67..56addca 100644 +--- a/peony-qt-desktop/desktop-menu.cpp ++++ b/peony-qt-desktop/desktop-menu.cpp +@@ -46,6 +46,8 @@ + #include <QMessageBox> + #include <QtConcurrent> + ++#include <file-launch-manager.h> ++ + #include <QUrl> + #include <QDir> + #include <QStandardPaths> +@@ -226,10 +228,9 @@ const QList<QAction *> DesktopMenu::constructOpenOpActions() + } + if (!dirs.isEmpty()) + this->openWindow(dirs); ++ + if (!files.isEmpty()) { +- for (auto uri : files) { +- FileLaunchManager::openAsync(uri); +- } ++ Peony::FileLaunchManager::openFilesByDefaultApplications(files); + } + }); + } +@@ -316,6 +317,7 @@ const QList<QAction *> DesktopMenu::constructCreateTemplateActions() + + QAction *action = new QAction(tmpIcon, qinfo.baseName(), this); + connect(action, &QAction::triggered, this, [=]() { ++ Q_EMIT markFilePos(mapToParent(this->pos())); + CreateTemplateOperation op(m_directory, CreateTemplateOperation::Template, t); + op.run(); + auto target = op.target(); +@@ -340,6 +342,7 @@ const QList<QAction *> DesktopMenu::constructCreateTemplateActions() + actions<<createEmptyFileAction; + connect(actions.last(), &QAction::triggered, this, [=]() { + //FileOperationUtils::create(m_directory); ++ Q_EMIT markFilePos(mapToParent(this->pos())); + CreateTemplateOperation op(m_directory); + op.run(); + auto targetUri = op.target(); +@@ -351,6 +354,7 @@ const QList<QAction *> DesktopMenu::constructCreateTemplateActions() + auto createFolderActions = new QAction(QIcon::fromTheme("folder-new-symbolic"), tr("Folder"), this); + actions<<createFolderActions; + connect(actions.last(), &QAction::triggered, this, [=]() { ++ Q_EMIT markFilePos(mapToParent(this->pos())); + //FileOperationUtils::create(m_directory, nullptr, CreateTemplateOperation::EmptyFolder); + CreateTemplateOperation op(m_directory, CreateTemplateOperation::EmptyFolder, tr("New Folder")); + op.run(); +@@ -371,39 +375,48 @@ const QList<QAction *> DesktopMenu::constructViewOpActions() + QList<QAction *> l; + + if (m_selections.isEmpty()) { ++ auto useAutoLayoutAction = addAction(tr("Auto arrange")); ++ useAutoLayoutAction->setCheckable(true); ++ useAutoLayoutAction->setChecked(GlobalSettings::getInstance()->getValue(DESKTOP_USE_AUTO_LAYOUT).toBool()); ++ connect(useAutoLayoutAction, &QAction::triggered, [=](bool checked){ ++ useAutoLayoutAction->setChecked(checked); ++ GlobalSettings::getInstance()->setGSettingValue(DESKTOP_USE_AUTO_LAYOUT, checked); ++ }); ++ l<<useAutoLayoutAction; ++ + auto viewTypeAction = addAction(tr("View Type")); + l<<viewTypeAction; + QMenu *viewTypeSubMenu = new QMenu(this); +- auto desktopView = dynamic_cast<DesktopIconView*>(m_view); ++ auto desktopView = dynamic_cast<AdvancedDesktopIconView*>(m_view); + auto zoomLevel = desktopView->zoomLevel(); + + auto smallAction = viewTypeSubMenu->addAction(tr("Small"), this, [=]() { +- Q_EMIT setDefaultZoomLevel(DesktopIconView::Small); ++ Q_EMIT setDefaultZoomLevel(AdvancedDesktopIconView::Small); + }); + auto normalAction = viewTypeSubMenu->addAction(tr("Normal"), this, [=]() { +- Q_EMIT setDefaultZoomLevel(DesktopIconView::Normal); ++ Q_EMIT setDefaultZoomLevel(AdvancedDesktopIconView::Normal); + }); + auto largeAction = viewTypeSubMenu->addAction(tr("Large"), this, [=]() { +- Q_EMIT setDefaultZoomLevel(DesktopIconView::Large); ++ Q_EMIT setDefaultZoomLevel(AdvancedDesktopIconView::Large); + }); + auto hugeAction = viewTypeSubMenu->addAction(tr("Huge"), this, [=]() { +- Q_EMIT setDefaultZoomLevel(DesktopIconView::Huge); ++ Q_EMIT setDefaultZoomLevel(AdvancedDesktopIconView::Huge); + }); + + switch (zoomLevel) { +- case DesktopIconView::Small: ++ case AdvancedDesktopIconView::Small: + smallAction->setCheckable(true); + smallAction->setChecked(true); + break; +- case DesktopIconView::Normal: ++ case AdvancedDesktopIconView::Normal: + normalAction->setCheckable(true); + normalAction->setChecked(true); + break; +- case DesktopIconView::Large: ++ case AdvancedDesktopIconView::Large: + largeAction->setCheckable(true); + largeAction->setChecked(true); + break; +- case DesktopIconView::Huge: ++ case AdvancedDesktopIconView::Huge: + hugeAction->setCheckable(true); + hugeAction->setChecked(true); + break; +@@ -443,32 +456,32 @@ const QList<QAction *> DesktopMenu::constructViewOpActions() + + sortTypeAction->setMenu(sortTypeMenu); + +- /* + //sort order +- auto sortOrderAction = addAction(tr("Sort Order...")); ++ auto sortOrderAction = addAction(tr("Sort Order")); + l<<sortOrderAction; + QMenu *sortOrderMenu = new QMenu(this); + tmp.clear(); +- tmp<<sortOrderMenu->addAction(tr("Ascending Order")); + tmp<<sortOrderMenu->addAction(tr("Descending Order")); +- // int sortOrder = m_view->getSortOrder(); +- // tmp.at(sortOrder)->setCheckable(true); +- // tmp.at(sortOrder)->setChecked(true); ++ tmp<<sortOrderMenu->addAction(tr("Ascending Order")); ++ int sortOrder = m_view->getSortOrder(); ++ tmp.at(sortOrder)->setCheckable(true); ++ tmp.at(sortOrder)->setChecked(true); + + for (int i = 0; i < tmp.count(); i++) { + connect(tmp.at(i), &QAction::triggered, this, [=](){ +- m_view->setSortOrder(i); ++ Q_EMIT setSortOrder(i); ++ GlobalSettings::getInstance()->setValue(DESKTOP_SORT_ORDER, i); + }); + } + + sortOrderAction->setMenu(sortOrderMenu); +- ++ /* + l<<addAction(QIcon::fromTheme("zoom-in-symbolic"), tr("Zoom &In"), this, [=](){ +- auto desktopView = dynamic_cast<DesktopIconView*>(m_view); ++ auto desktopView = dynamic_cast<AdvancedDesktopIconView*>(m_view); + desktopView->zoomIn(); + }); + l<<addAction(QIcon::fromTheme("zoom-out-symbolic"), tr("Zoom &Out"), this, [=](){ +- auto desktopView = dynamic_cast<DesktopIconView*>(m_view); ++ auto desktopView = dynamic_cast<AdvancedDesktopIconView*>(m_view); + desktopView->zoomOut(); + }); + */ +@@ -579,11 +592,14 @@ const QList<QAction *> DesktopMenu::constructFileOpActions() + }); + l<<addAction(QIcon::fromTheme("view-refresh-symbolic"), tr("Refresh")); + connect(l.last(), &QAction::triggered, this, [=]() { +- auto desktopView = dynamic_cast<DesktopIconView*>(m_view); ++ auto desktopView = dynamic_cast<AdvancedDesktopIconView*>(m_view); + desktopView->refresh(); + }); + } + ++ if (m_selections.isEmpty()) ++ addActions(FileOperationManager::getInstance()->getUndoRedoActions()); ++ + return l; + } + +diff --git a/peony-qt-desktop/desktop-menu.h b/peony-qt-desktop/desktop-menu.h +index 6986c49..9d05dfa 100644 +--- a/peony-qt-desktop/desktop-menu.h ++++ b/peony-qt-desktop/desktop-menu.h +@@ -24,7 +24,7 @@ + #define DESKTOPMENU_H + + #include <QMenu> +-#include "desktop-icon-view.h" ++#include "advanced-desktop-icon-view.h" + + namespace Peony { + +@@ -43,8 +43,10 @@ public: + void showProperties(const QStringList &uris); + + Q_SIGNALS: +- void setDefaultZoomLevel(Peony::DesktopIconView::ZoomLevel level); ++ void setDefaultZoomLevel(AdvancedDesktopIconView::ZoomLevel level); + void setSortType(int sortType); ++ void setSortOrder(int sortOrder); ++ void markFilePos(QPoint pos); + + protected: + void fillActions(); +diff --git a/peony-qt-desktop/desktop-window-manager.cpp b/peony-qt-desktop/desktop-window-manager.cpp +new file mode 100644 +index 0000000..88e68cd +--- /dev/null ++++ b/peony-qt-desktop/desktop-window-manager.cpp +@@ -0,0 +1,487 @@ ++/* ++ * Peony-Qt ++ * ++ * Copyright (C) 2024, KylinSoft Co., Ltd. ++ * ++ * 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 3 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, see <https://www.gnu.org/licenses/>. ++ * ++ * Authors: yangyanwei <yangyanwei@kylinos.cn> ++ * ++ */ ++ ++#include "desktop-window-manager.h" ++#include "desktopbackgroundwindow.h" ++#include "advanced-desktop-icon-view.h" ++#include "peony-desktop-application.h" ++#include "advanced-desktop-item-model.h" ++#include <KWindowSystem> ++#include <QScreen> ++#include "common.h" ++#include "file-info.h" ++#include "file-info-job.h" ++#include "file-meta-info.h" ++#include "global-settings.h" ++static int desktop_window_id = 0; ++static DesktopWindowManager *global_instance = nullptr; ++ ++DesktopWindowManager *DesktopWindowManager::getInstance() ++{ ++ if (!global_instance) { ++ global_instance = new DesktopWindowManager; ++ } ++ return global_instance; ++} ++ ++DesktopWindowManager::DesktopWindowManager(QObject *parent) : QObject(parent) ++{ ++ connect(kdk::WindowManager::self(),&kdk::WindowManager::windowAdded,this,[=](const kdk::WindowId& windowId){ ++ if((quint32)getpid() == kdk::WindowManager::getPid(windowId)) { ++ for (auto window : m_bgWindows) { ++ QString title = kdk::WindowManager::getWindowTitle(windowId); ++ QString windowTitle = window->windowTitle(); ++ if (title == windowTitle) { ++ window->setWindowId(windowId); ++ if (window->screen() == qApp->primaryScreen()) { ++ kdk::WindowManager::activateWindow(window->getWindowId()); ++ } ++ break; ++ } ++ } ++ } ++ }); ++ m_model = PeonyDesktopApplication::getModel(); ++ connect(m_model, &AdvancedDesktopItemModel::emitFinish, this, &DesktopWindowManager::initModelFinish); ++ m_desktopAutoLayout = GlobalSettings::getInstance()->getValue(DESKTOP_USE_AUTO_LAYOUT).toBool(); ++ connect(GlobalSettings::getInstance(), &GlobalSettings::valueChanged, this, [=](const QString &key){ ++ if (key == DESKTOP_USE_AUTO_LAYOUT) { ++ m_desktopAutoLayout = GlobalSettings::getInstance()->getValue(DESKTOP_USE_AUTO_LAYOUT).toBool(); ++ } ++ }); ++} ++ ++ ++DesktopWindowManager::~DesktopWindowManager() ++{ ++ for (auto window : m_bgWindows) { ++ delete window; ++ } ++ m_bgWindows.clear(); ++} ++ ++void DesktopWindowManager::addBgWindow(QScreen *screen) ++{ ++ int desktopWindowId = getDesktopWindowId(); ++ auto window = new DesktopBackgroundWindow(screen, desktopWindowId); ++ ++ m_bgWindows.append(window); ++ desktop_window_id = m_bgWindows.count(); ++ ++ qInfo()<<"[PeonyDesktopApplication::addBgWindow] screen name:"<<window->screen()->name()<<" IP:"<<window->screen() << "count:" << m_bgWindows.count(); ++ window->show(); ++ ++ if (m_initMetaInfo) { ++ m_mode = checkScreenMode(screen->geometry()); ++ if (2 == m_mode) { ++ multiscreenMode(); ++ } else { ++ singleScreenMode(); ++ } ++ relocateIconView(); ++ } else { ++ //在设置模式后初始化viewprt,否则会导致主屏是扩展屏,插入扩展屏后,model没有数据 ++ if (screen == qApp->primaryScreen()) { ++ window->getIconView()->refresh(); ++ } ++ } ++ ++ //task#74174 恢复扩展屏 ++ connect(screen, &QScreen::destroyed, this, [=](){ ++ if (m_mode == 2) { ++ if (m_bgWindows.count() > 2) { ++ //task#74174 销毁时保存扩展屏元素的坐标点 ++ m_model->saveExtendItemInfo(window->id()); ++ Q_EMIT getIconView(qApp->primaryScreen())->getIconView()->updateView(); ++ } else if (m_bgWindows.count() == 2) { ++ singleScreenMode(); ++ m_mode = 0; ++ } ++ } ++ qInfo()<<"QScreen::destroyed screen name:"<<screen->name()<< m_bgWindows.count(); ++ window->invaidScreen(); ++ bool sucess = m_bgWindows.removeOne(window); ++ qDebug()<<"QScreen::destroyed :"<<sucess; ++ delete window; ++ }); ++// connect(screen, &QScreen::destroyed, this, [=](){ ++// removeWindow(window); ++// }); ++ ++ connect(window, &DesktopBackgroundWindow::setDefaultZoomLevel, this, [=](AdvancedDesktopIconView::ZoomLevel level){ ++ for (auto bgWindow : m_bgWindows) { ++ if (bgWindow->getIconView()->zoomLevel() != level) { ++ bgWindow->getIconView()->setDefaultZoomLevel(level); ++ } ++ } ++ }); ++ //task#74174 更新排序方式 ++ connect(window, &DesktopBackgroundWindow::setSortType, this, [=](int sortType){ ++ clearAllRestoreInfo(); ++ for (auto bgWindow : m_bgWindows) { ++ bgWindow->getIconView()->setSortType(sortType); ++ } ++ }); ++ //task#74174 更新扩展屏与镜像切换 ++ connect(window, &DesktopBackgroundWindow::updateWindow, this, [=](const QRect &geometry){ ++ int mode = checkScreenMode(geometry); ++ if (m_mode != mode) { ++ if (1 == mode) { ++ singleScreenMode(); ++ } else if (2 == mode) { ++ multiscreenMode(); ++ } ++ } ++ m_mode = mode; ++ window->setWindowGeometry(geometry); ++ }); ++ connect(window, &DesktopBackgroundWindow::markFilePos, this, &DesktopWindowManager::markCreateFilePos); ++ ++ connect(window, &DesktopBackgroundWindow::clearOtherViewSelection, [=](){ ++ for (auto bgWindow : m_bgWindows) { ++ if (window == bgWindow) { ++ continue; ++ } ++ auto view = bgWindow->getIconView(); ++ view->clearAllIndexWidgets(); ++ view->clearSelection(); ++ } ++ }); ++ ++ connect(window, &DesktopBackgroundWindow::setSortOrder, this, [=](int sortOrder){ ++ for (auto bgWindow : m_bgWindows) { ++ bgWindow->getIconView()->setSortOrder(sortOrder); ++ } ++ }); ++ ++} ++ ++void DesktopWindowManager::removeWindow(DesktopBackgroundWindow *window) ++{ ++ QScreen *screen = qobject_cast<QScreen *>(sender()); ++ qInfo()<<"QScreen::destroyed screen name:"<<screen->name()<< m_bgWindows.count(); ++ if (m_mode == 2) { ++ if (m_bgWindows.count() > 2) { ++ //task#74174 销毁时保存扩展屏元素的坐标点 ++ m_model->saveExtendItemInfo(); ++ } else if (m_bgWindows.count() == 2) { ++ singleScreenMode(); ++ m_mode = 0; ++ } ++ } ++ bool sucess = m_bgWindows.removeOne(window); ++ qDebug()<<"QScreen::destroyed :"<<sucess; ++ delete window; ++} ++ ++void DesktopWindowManager::singleScreenMode() ++{ ++ auto primayView = getIconView(0); ++ ++ qDebug() << "primay view item" << primayView->model()->rowCount() << "total item:" << m_model->rowCount(); ++ if ( 2 == m_mode && primayView->model()->rowCount() == m_model->rowCount()) { ++ m_model->clearExtendItemPos(); ++ return; ++ } ++ m_model->getAllRestoreInfo(); ++ m_model->saveExtendItemInfo(); ++ for (auto bgWindow : m_bgWindows) { ++ auto view = bgWindow->getIconView(); ++ Q_EMIT view->updateView(); ++ } ++} ++ ++void DesktopWindowManager::multiscreenMode() ++{ ++ m_model->getAllRestoreInfo(); ++ m_model->resetExtendItemInfo(); ++ ++ for (auto bgWindow : m_bgWindows) { ++ auto view = bgWindow->getIconView(); ++ Q_EMIT view->updateView(); ++ } ++} ++ ++void DesktopWindowManager::relocateIconView() ++{ ++ qInfo()<<"start relocate icon view"; ++ //task#74174 更新多屏显示,根据id过滤元素 ++ int id = -1; ++ DesktopBackgroundWindow *primaryWindow = nullptr; ++ for (auto window : m_bgWindows) { ++ if (window->screen() == qApp->primaryScreen()) { ++ id = window->id(); ++ primaryWindow = window; ++ break; ++ } ++ } ++ qDebug()<<"primary screen id:"<<id; ++ if (0 < id) { ++ for (auto window : m_bgWindows) { ++ if (0 == window->id()) { ++ m_model->getAllRestoreInfo(); ++ window->setId(id); ++ primaryWindow->setId(0); ++ Q_EMIT window->getIconView()->updateView(); ++ Q_EMIT primaryWindow->getIconView()->updateView(); ++ KWindowSystem::raiseWindow(primaryWindow->winId()); ++ kdk::WindowManager::activateWindow(primaryWindow->getWindowId()); ++ return; ++ } ++ } ++ } ++ ++ for (auto window : m_bgWindows) { ++ qDebug() << "screen name :" << window->screen()->name() << " id:" << window->id() ; ++ if (window->screen() != qApp->primaryScreen()) { ++ KWindowSystem::raiseWindow(window->winId()); ++ kdk::WindowManager::activateWindow(window->getWindowId()); ++ } ++ } ++ if(primaryWindow) { ++ KWindowSystem::raiseWindow(primaryWindow->winId()); ++ kdk::WindowManager::activateWindow(primaryWindow->getWindowId()); ++ } ++} ++ ++int DesktopWindowManager::checkScreenMode(const QRect &geometry) ++{ ++ int mode = 1; ++ if (m_bgWindows.count() == 1) { ++ mode = 0; ++ return mode; ++ } ++ ++ for (auto window : m_bgWindows) { ++ if (window->screen()->geometry() != geometry) { ++ mode = 2; ++ break; ++ } ++ } ++ return mode; ++} ++ ++int DesktopWindowManager::getDesktopWindowId() ++{ ++ int desktopWindowId = 0; ++ for (; desktopWindowId <= desktop_window_id; desktopWindowId++) { ++ bool find = false; ++ for (auto bgWindow : m_bgWindows) { ++ if (bgWindow->id() == desktopWindowId) { ++ find = true; ++ break; ++ } ++ } ++ if (!find) { ++ break; ++ } ++ } ++ return desktopWindowId; ++} ++ ++void DesktopWindowManager::raiseWid() ++{ ++ QTimer::singleShot(2000, this, [=]() { ++ for (auto window : m_bgWindows) { ++ if (window->screen() == QGuiApplication::primaryScreen() && window->screen()) { ++ KWindowSystem::raiseWindow(window->winId()); ++ kdk::WindowManager::activateWindow(window->getWindowId()); ++ return; ++ } ++ } ++ }); ++} ++ ++DesktopBackgroundWindow *DesktopWindowManager::getIconView(QPoint pos) ++{ ++ //获取当前屏幕的view,如果是镜像直接返回主屏 ++ DesktopBackgroundWindow *window = getIconView(qApp->primaryScreen()); ++ QRegion screenRegion(qApp->primaryScreen()->geometry()); ++ if (screenRegion.contains(pos)) { ++ return window; ++ }; ++ ++ for (auto bgWindow : m_bgWindows) { ++ QRegion screenRegion(bgWindow->screen()->geometry()); ++ if (screenRegion.contains(pos)) { ++ window = bgWindow; ++ break; ++ }; ++ } ++ return window; ++} ++ ++AdvancedDesktopIconView *DesktopWindowManager::getIconView(int id) ++{ ++ for (auto window : m_bgWindows) { ++ if (id == window->id()) { ++ return window->getIconView(); ++ } ++ } ++ return m_bgWindows[0]->getIconView(); ++} ++ ++DesktopBackgroundWindow *DesktopWindowManager::getIconView(QScreen *screen) ++{ ++ for (auto window : m_bgWindows) { ++ if (screen == window->screen()) { ++ return window; ++ } ++ } ++ return m_bgWindows[0]; ++} ++ ++void DesktopWindowManager::layoutViewItems() ++{ ++ for (auto bgWindow : m_bgWindows) { ++ auto view = bgWindow->getIconView(); ++ view->layoutItems(); ++ } ++} ++ ++void DesktopWindowManager::initModelFinish() ++{ ++ if (m_initMetaInfo) { ++ return; ++ } ++ ++ if (m_bgWindows.count() < 1) ++ return; ++ ++ m_mode = checkScreenMode(m_bgWindows[0]->screen()->geometry()); ++ if (2 == m_mode) { ++ multiscreenMode(); ++ } else { ++ singleScreenMode(); ++ } ++ relocateIconView(); ++ m_initMetaInfo = true; ++ ++ Q_EMIT emitFinish(); ++} ++ ++//QPoint DesktopWindowManager::postion(const QString &uri) ++//{ ++// for (int i = 0; i < m_model->rowCount(); i++) { ++// auto index = m_model->index(i, 0); ++// QString currentUri = index.data(Qt::UserRole).toString(); ++// if (uri == currentUri) { ++//// auto metaInfo = FileMetaInfo::fromUri(uri); ++//// if (metaInfo) { ++//// QStringList strPos = metaInfo->getMetaInfoStringList(ITEM_GRID_POS_ATTRIBUTE); ++//// if (strPos.count() == 2) { ++//// QPoint pos(strPos[0].toInt(), strPos[1].toInt()); ++//// return pos; ++//// } ++//// } ++// QPoint pos = index.data(AdvancedDesktopItemModel::PositionRole).toPoint(); ++// return pos; ++// } ++// } ++// return QPoint(-1, -1); ++//} ++ ++//QMap<QString, QPoint> DesktopWindowManager::getAllItemPos(const QStringList &uris) ++//{ ++// QMap<QString, QPoint> itemPosMap; ++// Peony::AdvancedDesktopItemModel *model = PeonyDesktopApplication::getModel(); ++// for (int i = 0; i < model->rowCount(); i++) { ++// auto index = model->index(i, 0); ++// QString currentUri = index.data(Qt::UserRole).toString(); ++// if (uris.contains(currentUri)) { ++//// auto metaInfo = FileMetaInfo::fromUri(currentUri); ++//// if (metaInfo) { ++//// QStringList strPos = metaInfo->getMetaInfoStringList(ITEM_GRID_POS_ATTRIBUTE); ++//// if (strPos.count() == 2) { ++//// QPoint pos(strPos[0].toInt(), strPos[1].toInt()); ++ ++//// } ++//// } ++// QPoint pos = index.data(AdvancedDesktopItemModel::PositionRole).toPoint(); ++// itemPosMap.insert(currentUri, pos); ++// } ++// } ++// return itemPosMap; ++//} ++ ++void DesktopWindowManager::markCreateFilePos(const QPoint &pos) ++{ ++ //如果当前创建文件的屏幕满了,则找到一个空的屏幕,按照顺序添加 ++ DesktopBackgroundWindow *window = qobject_cast<DesktopBackgroundWindow *>(sender()); ++ //鼠标坐标点转化成网格标点; ++ QPoint tmpPos = window->getIconView()->getGridPosFromMousePos(pos); ++ m_markMousePressPos = window->getIconView()->checkGridPos(tmpPos); ++ m_markMousePressId = window->id(); ++ if (window->getIconView()->isFull()) { ++ for (auto bgWindow : m_bgWindows) { ++ bool isFull = bgWindow->getIconView()->isFull(); ++ if (!isFull) { ++ m_markMousePressPos = QPoint(0, 0); ++ m_markMousePressId = bgWindow->id(); ++// bgWindow->getIconView()->markFilePos(m_markMousePressPos); ++ return; ++ } ++ } ++ m_markMousePressPos = QPoint(0, 0); ++ m_markMousePressId = window->id(); ++ } ++// window->getIconView()->markFilePos(m_markMousePressPos); ++} ++ ++void DesktopWindowManager::createIdAndPos(int *id, QPoint *pos) ++{ ++ if (m_markMousePressPos == QPoint(-1, -1)) { ++ auto window = getIconView(QCursor::pos()); ++ bool isFull = window->getIconView()->isFull(); ++ if (!isFull) { ++ *id = window->id(); ++ *pos = m_markMousePressPos; ++ return; ++ } ++ for (auto window : m_bgWindows) { ++ bool isFull = window->getIconView()->isFull(); ++ if (!isFull) { ++ *id = window->id(); ++ *pos = m_markMousePressPos; ++ return; ++ } ++ } ++ *id = window->id(); ++ *pos = QPoint(0, 0); ++ return; ++ } ++ ++ *id = m_markMousePressId; ++ *pos = m_desktopAutoLayout? QPoint(0, 0): m_markMousePressPos;//自动排列的情况下清空位置的记录 ++ m_markMousePressPos = QPoint(-1, -1); ++ m_markMousePressId = 0; ++ return; ++} ++ ++void DesktopWindowManager::clearAllRestoreInfo() ++{ ++ for (int i = 0; i < m_model->rowCount(); i++) { ++ auto index = m_model->index(i, 0); ++ m_model->setData(index, QStringList(""), AdvancedDesktopIconView::ExceptedPositionRole); ++ } ++} ++ +diff --git a/peony-qt-desktop/desktop-window-manager.h b/peony-qt-desktop/desktop-window-manager.h +new file mode 100644 +index 0000000..f1eaa04 +--- /dev/null ++++ b/peony-qt-desktop/desktop-window-manager.h +@@ -0,0 +1,85 @@ ++/* ++ * Peony-Qt ++ * ++ * Copyright (C) 2024, KylinSoft Co., Ltd. ++ * ++ * 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 3 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, see <https://www.gnu.org/licenses/>. ++ * ++ * Authors: yangyanwei <yangyanwei@kylinos.cn> ++ * ++ */ ++ ++#ifndef DESKTOPWINDOWMANAGER_H ++#define DESKTOPWINDOWMANAGER_H ++ ++#include <QObject> ++#include <QList> ++#include <QScreen> ++#include <QMap> ++class DesktopBackgroundWindow; ++class AdvancedDesktopIconView; ++class QPoint; ++namespace Peony { ++class AdvancedDesktopItemModel; ++} ++class DesktopWindowManager : public QObject ++{ ++ Q_OBJECT ++public: ++ explicit DesktopWindowManager(QObject *parent = nullptr); ++ ~DesktopWindowManager(); ++ ++ static DesktopWindowManager *getInstance(); ++ QPoint postion(const QString &uri); ++ QMap<QString, QPoint> getAllItemPos(const QStringList &uris); ++ void createIdAndPos(int *id, QPoint *pos); ++ void markCreateFilePos(const QPoint &pos); ++ ++public Q_SLOTS: ++ void addBgWindow(QScreen *screen); ++ void removeWindow(DesktopBackgroundWindow *window); ++ ++ void singleScreenMode(); ++ ++ void multiscreenMode(); ++ ++ void relocateIconView(); ++ ++ DesktopBackgroundWindow *getIconView(QPoint pos); ++ AdvancedDesktopIconView *getIconView(int id); ++ DesktopBackgroundWindow *getIconView(QScreen *screen); ++ ++ void initModelFinish(); ++ int checkScreenMode(const QRect &geometry); ++ void raiseWid(); ++ void layoutViewItems(); ++ void clearAllRestoreInfo(); ++ ++private: ++ int getDesktopWindowId(); ++ ++Q_SIGNALS: ++ void emitFinish(); ++ ++private: ++ QList<DesktopBackgroundWindow *> m_bgWindows; ++ Peony::AdvancedDesktopItemModel *m_model = nullptr; ++ QPoint m_markMousePressPos = QPoint(-1, -1); ++ int m_markMousePressId = 0; ++ int m_mode = 0; ++ bool m_initMetaInfo = false; ++ bool m_desktopAutoLayout = false; ++}; ++ ++#endif // DESKTOPWINDOWMANAGER_H +diff --git a/peony-qt-desktop/desktopbackgroundwindow.cpp b/peony-qt-desktop/desktopbackgroundwindow.cpp +index 3863f5a..c12080c 100644 +--- a/peony-qt-desktop/desktopbackgroundwindow.cpp ++++ b/peony-qt-desktop/desktopbackgroundwindow.cpp +@@ -42,7 +42,7 @@ static QTimeLine *gTimeLine = nullptr; + + static bool startup = false; + +-DesktopBackgroundWindow::DesktopBackgroundWindow(const KScreen::OutputPtr &output, int desktopWindowId, QWidget *parent) : m_output(output), QMainWindow(parent) ++DesktopBackgroundWindow::DesktopBackgroundWindow(QScreen *screen, int desktopWindowId, QWidget *parent) : QMainWindow(parent) + { + if (!gTimeLine) { + gTimeLine = new QTimeLine(100); +@@ -60,13 +60,17 @@ DesktopBackgroundWindow::DesktopBackgroundWindow(const KScreen::OutputPtr &outpu + + setContextMenuPolicy(Qt::CustomContextMenu); + +- //m_screen = screen; +- m_desktopIconView = new Peony::DesktopIconView(this); ++ m_screen = screen; ++ m_desktopIconView = new AdvancedDesktopIconView(this); + m_desktopIconView->setId(desktopWindowId); + m_id = desktopWindowId; +- move(getLogicalGeometryFromScreen().topLeft()); +- setFixedSize(getLogicalGeometryFromScreen().size()); ++ move(screen->geometry().topLeft()); ++ setFixedSize(screen->geometry().size()); + setContentsMargins(0, 0, 0, 0); ++ m_desktopIconView->resize(screen->geometry().size()); ++ setCentralWidget(m_desktopIconView); ++ ++ connect(screen, &QScreen::geometryChanged, this, QOverload<const QRect&>::of(&DesktopBackgroundWindow::updateWindow)); + + auto manager = DesktopBackgroundManager::globalInstance(); + connect(manager, &DesktopBackgroundManager::screensUpdated, this, QOverload<>::of(&DesktopBackgroundWindow::update)); +@@ -76,8 +80,6 @@ DesktopBackgroundWindow::DesktopBackgroundWindow(const KScreen::OutputPtr &outpu + } + + connect(this, &QWidget::customContextMenuRequested, this, [=](const QPoint &pos){ +- if (centralWidget() != m_desktopIconView) +- return; + QPoint relativePos = getRelativePos(pos); + qInfo()<<pos; + // fix #115384, context menu key issue +@@ -100,6 +102,10 @@ DesktopBackgroundWindow::DesktopBackgroundWindow(const KScreen::OutputPtr &outpu + m_menu = new DesktopMenu(m_desktopIconView, this); + connect(m_menu, &DesktopMenu::setDefaultZoomLevel, this, &DesktopBackgroundWindow::setDefaultZoomLevel); + connect(m_menu, &DesktopMenu::setSortType, this, &DesktopBackgroundWindow::setSortType); ++ connect(m_menu, &DesktopMenu::setSortOrder, this, &DesktopBackgroundWindow::setSortOrder); ++ connect(m_menu, &DesktopMenu::markFilePos, this, [=](){ ++ markFilePos(relativePos); ++ }); + + if (m_desktopIconView->getSelections().isEmpty()) { + auto action = m_menu->addAction(QObject::tr("Set Background")); +@@ -115,20 +121,31 @@ DesktopBackgroundWindow::DesktopBackgroundWindow(const KScreen::OutputPtr &outpu + + } + +- for (auto screen : qApp->screens()) { +- if (screen->geometry().contains(relativePos)); +- //menu.windowHandle()->setScreen(screen); +- } ++// for (auto screen : qApp->screens()) { ++// if (screen->geometry().contains(relativePos)); ++// //menu.windowHandle()->setScreen(screen); ++// } + + /* 菜单执行弹出操作时停止更新,超过1s或者结束菜单都启用更新; 解决:点击鼠标右键,右键菜单会闪烁(偶现) */ +- setUpdatesEnabled(false); +- QTimer::singleShot(1000, this, [=](){ +- if(!updatesEnabled()){ +- setUpdatesEnabled(true); +- } +- }); +- m_menu->exec(mapToGlobal(pos)); +- setUpdatesEnabled(true);//end ++// if (manager->AnimationRunning()) { ++// //动画过程立刻更新界面 ++// setUpdatesEnabled(true); ++// repaint(); ++// m_menu->exec(mapToGlobal(pos)); ++// } else { ++// setUpdatesEnabled(false); ++// QTimer::singleShot(1000, this, [=](){ ++// if(!updatesEnabled()){ ++// setUpdatesEnabled(true); ++// } ++// }); ++ //fix bug#250273,right menu not show complete issue ++ QScreen *screen=qApp->primaryScreen(); ++ QRect geometry = screen->availableGeometry(); ++ m_menu->setMaximumHeight(geometry.height()); ++ m_menu->exec(mapToGlobal(pos)); ++// setUpdatesEnabled(true);//end ++// } + + auto urisToEdit = m_menu->urisToEdit(); + m_desktopIconView->UpdateToEditUris(urisToEdit); +@@ -142,36 +159,8 @@ DesktopBackgroundWindow::DesktopBackgroundWindow(const KScreen::OutputPtr &outpu + }); + }); + +- +- connect(m_output.data(), &KScreen::Output::posChanged, +- this, [=](){ +- qDebug() << "output posChanged:" << output.data()->name()<< output->id() << output->geometry(); +- setWindowGeometry(getLogicalGeometryFromScreen()); +- }); +- +- connect(m_output.data(), &KScreen::Output::currentModeIdChanged, +- this, [=](){ +- qDebug() << "output currentModeIdChanged:" << output.data()->name()<< output->id() << output->geometry(); +- setWindowGeometry(getLogicalGeometryFromScreen()); +- }); +- +- connect(m_output.data(), &KScreen::Output::rotationChanged, +- this, [=](){ +- qDebug() << "output rotationChanged:" << output.data()->name()<< output->id() << output->geometry(); +- setWindowGeometry(getLogicalGeometryFromScreen()); +- }); +- +- connect(m_output.data(), &KScreen::Output::sizeChanged, +- this, [=](){ +- qDebug() << "output sizeChanged:" << output.data()->name()<< output->id() << output->geometry(); +- setWindowGeometry(getLogicalGeometryFromScreen()); +- }); +- +- connect(m_output.data(), &KScreen::Output::scaleChanged, +- this, [=](){ +- qDebug() << "output scaleChanged:" << m_output.data()->name()<< m_output->id() << m_output->geometry(); +- setWindowGeometry(m_output->geometry()); +- }); ++ connect(m_desktopIconView, &AdvancedDesktopIconView::clearOtherViewSelection, this, &DesktopBackgroundWindow::clearOtherViewSelection); ++ connect(m_desktopIconView, &AdvancedDesktopIconView::setZoomLevel, this, &DesktopBackgroundWindow::setDefaultZoomLevel); + } + + DesktopBackgroundWindow::~DesktopBackgroundWindow() +@@ -185,7 +174,7 @@ void DesktopBackgroundWindow::paintEvent(QPaintEvent *event) + if (!manager->getPaintBackground()) + return; + +- if (m_output.isNull()) ++ if (!m_screen) + return; + + QPainter p(this); +@@ -197,19 +186,19 @@ void DesktopBackgroundWindow::paintEvent(QPaintEvent *event) + p.setRenderHint(QPainter::SmoothPixmapTransform); + auto animation = manager->getAnimation(); + QPixmap frontPixmap = manager->getFrontPixmap(); +- QSize screenSize = getLogicalGeometryFromScreen().size(); ++ + if (animation->state() == QVariantAnimation::Running) { + auto opacity = animation->currentValue().toReal(); + QPixmap backPixmap = manager->getBackPixmap(); + + if (manager->getBackgroundOption() == "centered") { + //居中 +- p.drawPixmap((screenSize.width() - backPixmap.rect().width()) / 2, +- (screenSize.height() - backPixmap.rect().height()) / 2, ++ p.drawPixmap((m_screen->size().width() - backPixmap.rect().width()) / 2, ++ (m_screen->size().height() - backPixmap.rect().height()) / 2, + backPixmap); + p.setOpacity(opacity); +- p.drawPixmap((screenSize.width() - frontPixmap.rect().width()) / 2, +- (screenSize.height() - frontPixmap.rect().height()) / 2, ++ p.drawPixmap((m_screen->size().width() - frontPixmap.rect().width()) / 2, ++ (m_screen->size().height() - frontPixmap.rect().height()) / 2, + frontPixmap); + } else if (manager->getBackgroundOption() == "stretched") { + //拉伸 +@@ -230,12 +219,12 @@ void DesktopBackgroundWindow::paintEvent(QPaintEvent *event) + while (1) { + p.drawPixmap(drawedWidth, drawedHeight, backPixmap); + drawedWidth += backPixmap.width(); +- if (drawedWidth >= screenSize.width()) { ++ if (drawedWidth >= m_screen->size().width()) { + break; + } + } + drawedHeight += backPixmap.height(); +- if (drawedHeight >= screenSize.height()) { ++ if (drawedHeight >= m_screen->size().height()) { + break; + } + } +@@ -247,12 +236,12 @@ void DesktopBackgroundWindow::paintEvent(QPaintEvent *event) + while (1) { + p.drawPixmap(drawedWidth, drawedHeight, frontPixmap); + drawedWidth += frontPixmap.width(); +- if (drawedWidth >= screenSize.width()) { ++ if (drawedWidth >= m_screen->size().width()) { + break; + } + } + drawedHeight += frontPixmap.height(); +- if (drawedHeight >= screenSize.height()) { ++ if (drawedHeight >= m_screen->size().height()) { + break; + } + } +@@ -263,9 +252,9 @@ void DesktopBackgroundWindow::paintEvent(QPaintEvent *event) + p.drawPixmap(getDestRect(frontPixmap), frontPixmap, frontPixmap.rect()); + } else if (manager->getBackgroundOption() == "spanned") { + //跨区 +- auto geometry = getLogicalGeometryFromScreen(); ++ p.drawPixmap(this->rect(), backPixmap, getSourceRect(backPixmap, m_screen->geometry())); + p.setOpacity(opacity); +- p.drawPixmap(this->rect(), frontPixmap, getSourceRect(frontPixmap, geometry)); ++ p.drawPixmap(this->rect(), frontPixmap, getSourceRect(frontPixmap, m_screen->geometry())); + } else { + p.drawPixmap(rect().adjusted(0, 0, -1, -1), backPixmap, backPixmap.rect()); + p.setOpacity(opacity); +@@ -274,8 +263,8 @@ void DesktopBackgroundWindow::paintEvent(QPaintEvent *event) + + } else { + if (manager->getBackgroundOption() == "centered") { +- p.drawPixmap((screenSize.width() - frontPixmap.rect().width()) / 2, +- (screenSize.height() - frontPixmap.rect().height()) / 2, ++ p.drawPixmap((m_screen->size().width() - frontPixmap.rect().width()) / 2, ++ (m_screen->size().height() - frontPixmap.rect().height()) / 2, + frontPixmap); + } else if (manager->getBackgroundOption() == "stretched") { + p.drawPixmap(this->rect(), frontPixmap, frontPixmap.rect()); +@@ -291,20 +280,19 @@ void DesktopBackgroundWindow::paintEvent(QPaintEvent *event) + while (1) { + p.drawPixmap(drawedWidth, drawedHeight, frontPixmap); + drawedWidth += frontPixmap.width(); +- if (drawedWidth >= screenSize.width()) { ++ if (drawedWidth >= m_screen->size().width()) { + break; + } + } + drawedHeight += frontPixmap.height(); +- if (drawedHeight >= screenSize.height()) { ++ if (drawedHeight >= m_screen->size().height()) { + break; + } + } + } else if (manager->getBackgroundOption() == "zoom") { + p.drawPixmap(getDestRect(frontPixmap), frontPixmap, frontPixmap.rect()); + } else if (manager->getBackgroundOption() == "spanned") { +- auto geometry = getLogicalGeometryFromScreen(); +- p.drawPixmap(this->rect(), frontPixmap, getSourceRect(frontPixmap, geometry)); ++ p.drawPixmap(this->rect(), frontPixmap, getSourceRect(frontPixmap, m_screen->geometry())); + } else { + p.drawPixmap(rect().adjusted(0, 0, -1, -1), frontPixmap, frontPixmap.rect()); + } +@@ -323,28 +311,14 @@ void DesktopBackgroundWindow::paintEvent(QPaintEvent *event) + } + } + +-KScreen::OutputPtr DesktopBackgroundWindow::screen() const ++QScreen *DesktopBackgroundWindow::screen() const + { +- return m_output; +-} +- +-QRect DesktopBackgroundWindow::getLogicalGeometryFromScreen() +-{ +- if (!m_output.isNull() && m_output->isEnabled()) { +- if (QApplication::platformName().toLower().contains("wayland")) { +- return m_output->geometry(); +- } +- qreal ratio = this->windowHandle()->devicePixelRatio(); +- QRect rect = QRect(m_output->geometry().topLeft()/ratio, m_output->geometry().size()/ratio); +- return rect; +- } else { +- return this->geometry(); +- } ++ return m_screen; + } + + void DesktopBackgroundWindow::invaidScreen() + { +- m_output = nullptr; ++ m_screen = nullptr; + } + + bool DesktopBackgroundWindow::event(QEvent *event) +@@ -359,8 +333,8 @@ bool DesktopBackgroundWindow::event(QEvent *event) + m_shellSurface->setSkipSwitcher(true); + m_shellSurface->setSkipTaskbar(true); + // wayland中构造函数的move只能在这里生效 +- if (!m_output.isNull()) { +- m_shellSurface->setPosition(getLogicalGeometryFromScreen().topLeft()); ++ if (m_screen) { ++ m_shellSurface->setPosition(m_screen->geometry().topLeft()); + } + } + break; +@@ -381,7 +355,7 @@ bool DesktopBackgroundWindow::event(QEvent *event) + + void DesktopBackgroundWindow::setWindowGeometry(const QRect &geometry) + { +- qInfo()<<"bg window geometry changed"<<geometry; ++ qInfo()<<"bg window geometry changed"<<screen()->name()<<geometry<<screen()->geometry(); + if (gTimeLine->state() != QTimeLine::Running) { + gTimeLine->start(); + } else { +@@ -391,22 +365,21 @@ void DesktopBackgroundWindow::setWindowGeometry(const QRect &geometry) + + void DesktopBackgroundWindow::updateWindowGeometry() + { +- if (m_output.isNull()) { ++ if (!m_screen) { + return; + } +- int mode = ((PeonyDesktopApplication*)qApp)->checkScreenMode(m_output->geometry()); +- ((PeonyDesktopApplication*)qApp)->changeMode(mode); +- auto geometry = getLogicalGeometryFromScreen(); ++ auto geometry = m_screen->geometry(); + move(geometry.topLeft()); + if (m_shellSurface) { + m_shellSurface->setPosition(geometry.topLeft()); + } + setFixedSize(geometry.size()); + +- qInfo()<<"bg window geometry changed slot"<<screen().data()->name()<<geometry; ++ qInfo()<<"bg window geometry changed slot"<<screen()->name()<<geometry; ++ + // raise primary window to make sure icon view is visible. + if (centralWidget()) { +- if (screen()->isPrimary()) { ++ if (screen() == qApp->primaryScreen()) { + qInfo()<<"has center widget, raise window"; + KWindowSystem::raiseWindow(this->winId()); + kdk::WindowManager::activateWindow(m_windowId); +@@ -441,11 +414,11 @@ kdk::WindowId DesktopBackgroundWindow::getWindowId() + //获取iconview中图标的相对位置 + QPoint DesktopBackgroundWindow::getRelativePos(const QPoint &pos) + { +- if (m_output.isNull()) { ++ if (!m_screen) { + return pos; + } + QPoint relativePos = pos; +- if (m_output->isPrimary()) { ++ if (m_screen == QApplication::primaryScreen()) { + if (m_panelSetting) { + int position = m_panelSetting->get("panelposition").toInt(); + int offset = m_panelSetting->get("panelsize").toInt(); +@@ -528,12 +501,11 @@ QRect DesktopBackgroundWindow::getSourceRect(const QPixmap &pixmap) + + QRect DesktopBackgroundWindow::getSourceRect(const QPixmap &pixmap, const QRect &screenGeometry) + { +- QRegion virtualScreensRegion; +- for (auto qscreen : qApp->screens()) { +- virtualScreensRegion += qscreen->geometry(); ++ QRegion region; ++ for (auto screen : qApp->screens()) { ++ region += screen->geometry(); + } +- QRect virtualGeometry = virtualScreensRegion.boundingRect().translated(0, 0); +- ++ QRect virtualGeometry = region.boundingRect().translated(0, 0); + qreal pixWidth = pixmap.width(); + qreal pixHeight = pixmap.height(); + +@@ -604,16 +576,7 @@ QRect DesktopBackgroundWindow::getDestRect(const QPixmap &pixmap) + return QRect(offsetPoint, sourceSize); + } + +-Peony::DesktopIconView *DesktopBackgroundWindow::getIconView() ++AdvancedDesktopIconView *DesktopBackgroundWindow::getIconView() + { + return m_desktopIconView; + } +- +-void DesktopBackgroundWindow::setCentralView() +-{ +- if (centralWidget()) { +- takeCentralWidget(); +- } +- +- setCentralWidget(m_desktopIconView); +-} +diff --git a/peony-qt-desktop/desktopbackgroundwindow.h b/peony-qt-desktop/desktopbackgroundwindow.h +index ab3b4d1..56e29ec 100644 +--- a/peony-qt-desktop/desktopbackgroundwindow.h ++++ b/peony-qt-desktop/desktopbackgroundwindow.h +@@ -25,8 +25,7 @@ + + #include <QMainWindow> + #include <QGSettings> +-#include "desktop-icon-view.h" +-#include <KF5/KScreen/kscreen/output.h> ++#include "advanced-desktop-icon-view.h" + #include "windowmanager/windowmanager.h" + + namespace KWayland { +@@ -42,31 +41,30 @@ class DesktopBackgroundWindow : public QMainWindow + { + Q_OBJECT + public: +- explicit DesktopBackgroundWindow(const KScreen::OutputPtr &output, int desktopWindowId, QWidget *parent = nullptr); ++ explicit DesktopBackgroundWindow(QScreen *screen, int desktopWindowId, QWidget *parent = nullptr); + ~DesktopBackgroundWindow() override; + + int id() const; + +- KScreen::OutputPtr screen() const; +- QRect getLogicalGeometryFromScreen(); +- +- Peony::DesktopIconView *getIconView(); ++ QScreen *screen() const; ++ AdvancedDesktopIconView *getIconView(); + void setId(int id); + void setWindowId(kdk::WindowId id); + kdk::WindowId getWindowId(); + + bool event(QEvent *event) override; ++ void invaidScreen(); + + Q_SIGNALS: +- void setDefaultZoomLevel(Peony::DesktopIconView::ZoomLevel level); ++ void setDefaultZoomLevel(AdvancedDesktopIconView::ZoomLevel level); + void setSortType(int sortType); +- void destroyed(); ++ void setSortOrder(int sortOrder); ++ void updateWindow(const QRect &geometry); ++ void markFilePos(QPoint pos); ++ void clearOtherViewSelection(); + + public Q_SLOTS: + void setWindowGeometry(const QRect &geometry); +- void invaidScreen(); +- +- void setCentralView(); + + protected Q_SLOTS: + void updateWindowGeometry(); +@@ -86,13 +84,12 @@ protected: + + private: + int m_id = -1; +- //QScreen *m_screen = nullptr; ++ QScreen *m_screen = nullptr; + QGSettings *m_panelSetting = nullptr; +- Peony::DesktopIconView *m_desktopIconView = nullptr; ++ AdvancedDesktopIconView *m_desktopIconView = nullptr; + + KWayland::Client::PlasmaShellSurface *m_shellSurface = nullptr; + Peony::DesktopMenu *m_menu = nullptr; +- KScreen::OutputPtr m_output = nullptr; + kdk::WindowId m_windowId; + }; + +diff --git a/peony-qt-desktop/main.cpp b/peony-qt-desktop/main.cpp +index cdbb0c4..2a32077 100644 +--- a/peony-qt-desktop/main.cpp ++++ b/peony-qt-desktop/main.cpp +@@ -87,6 +87,13 @@ void messageOutput(QtMsgType type, const QMessageLogContext &context, const QStr + int main(int argc, char *argv[]) + { + //qputenv("QT_QPA_PLATFORM", "wayland"); ++ ++ //bug#249961, limit set to wayland product ++ //bug#267883, comment set code to avoid new bug ++// bool isWayland = QString(qgetenv("XDG_SESSION_DESKTOP")).contains("ukui-wayland"); ++// if (isWayland) ++// qunsetenv("SESSION_MANAGER"); ++ + PeonyDesktopApplication::peony_desktop_start_time = QDateTime::currentMSecsSinceEpoch(); + + QString xdgUserDirsUri = "file://" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/.config/user-dirs.dirs"; +diff --git a/peony-qt-desktop/peony-dbus-service.cpp b/peony-qt-desktop/peony-dbus-service.cpp +index ea81198..b15701e 100644 +--- a/peony-qt-desktop/peony-dbus-service.cpp ++++ b/peony-qt-desktop/peony-dbus-service.cpp +@@ -20,7 +20,7 @@ + * + */ + +-#include "desktop-icon-view.h" ++#include "advanced-desktop-icon-view.h" + #include "peony-dbus-service.h" + #include <QDBusConnection> + #include <QDBusConnectionInterface> +@@ -28,7 +28,7 @@ + #include <QDebug> + using namespace Peony; + +-PeonyDbusService::PeonyDbusService(DesktopIconView *view, QObject *parent) : QObject(parent) ++PeonyDbusService::PeonyDbusService(AdvancedDesktopIconView *view, QObject *parent) : QObject(parent) + { + m_desktopIconView = view; + } +diff --git a/peony-qt-desktop/peony-dbus-service.h b/peony-qt-desktop/peony-dbus-service.h +index b04e5bb..6202807 100644 +--- a/peony-qt-desktop/peony-dbus-service.h ++++ b/peony-qt-desktop/peony-dbus-service.h +@@ -23,7 +23,6 @@ + #ifndef PEONYDBUSSERVICE_H + #define PEONYDBUSSERVICE_H + +-#include "desktop-icon-view.h" + #include <QCoreApplication> + #include <QTimer> + #include <QtDBus> +@@ -37,10 +36,9 @@ + 方法:GetSecurityConfigPath()//获取安全配置文件存放路径 + ReloadSecurityConfig()// 重新加载安全配置 + */ +- ++class AdvancedDesktopIconView; + namespace Peony { + +-class DesktopIconView; + + class PeonyDbusService:public QObject + { +@@ -48,7 +46,7 @@ class PeonyDbusService:public QObject + Q_CLASSINFO("D-Bus Interface", "org.ukui.peony") + + public: +- explicit PeonyDbusService(DesktopIconView *view, QObject *parent=nullptr); ++ explicit PeonyDbusService(AdvancedDesktopIconView *view, QObject *parent=nullptr); + ~PeonyDbusService(); + + void DbusServerRegister(); +@@ -68,7 +66,7 @@ public Q_SLOTS: + void sendEngrampaOpreateFinishSig(const QString& path, bool finish); + + private: +- DesktopIconView *m_desktopIconView = nullptr; ++ AdvancedDesktopIconView *m_desktopIconView = nullptr; + }; + + } +diff --git a/peony-qt-desktop/peony-desktop-application.cpp b/peony-qt-desktop/peony-desktop-application.cpp +index 65cbfb4..ffd7f7c 100644 +--- a/peony-qt-desktop/peony-desktop-application.cpp ++++ b/peony-qt-desktop/peony-desktop-application.cpp +@@ -28,8 +28,6 @@ + + #include "volume-manager.h" + +-#include "desktop-icon-view.h" +- + #include "global-settings.h" + #include "plasma-shell-manager.h" + #include "desktop-menu.h" +@@ -37,7 +35,8 @@ + #include "file-enumerator.h" + #include "desktop-background-manager.h" + #include "desktopbackgroundwindow.h" +-#include "desktop-item-model.h" ++#include "advanced-desktop-item-model.h" ++#include "desktop-window-manager.h" + #include "windowmanager/windowmanager.h" + + #include <QCommandLineParser> +@@ -71,8 +70,6 @@ + #include <QX11Info> + #include <X11/Xlib.h> + +-#include <QMessageBox> +- + #define KYLIN_USER_GUIDE_PATH "/" + #define KYLIN_USER_GUIDE_SERVICE QString("com.kylinUserGuide.hotel_%1").arg(getuid()) + #define KYLIN_USER_GUIDE_INTERFACE "com.guide.hotel" +@@ -83,8 +80,8 @@ static bool has_desktop = false; + static bool has_daemon = false; + static bool has_background = false; + static QRect max_size = QRect(0, 0, 0, 0); +-static Peony::DesktopItemModel *desktop_model = nullptr; +-static int desktop_window_id = 0; ++static Peony::AdvancedDesktopItemModel *desktop_model = nullptr; ++static DesktopWindowManager *desktopManger = nullptr; + static bool g_emitFinish = false; + + //record of desktop start time +@@ -210,6 +207,7 @@ QRect caculateVirtualDesktopGeometry() { + + PeonyDesktopApplication::PeonyDesktopApplication(int &argc, char *argv[], const QString &applicationName) : QtSingleApplication (applicationName, argc, argv) + { ++ GlobalSettings::getInstance()->setDesktopStartUp(true); + // fix #172774 + QIcon::setFallbackSearchPaths(QIcon::fallbackSearchPaths()<<"/usr/share/pixmaps"); + +@@ -264,6 +262,32 @@ PeonyDesktopApplication::PeonyDesktopApplication(int &argc, char *argv[], const + + if (!this->isRunning()) { + qDebug()<<"isPrimary screen"; ++ getDesktopWindowManager(); ++ ++ //check if is great wall device and init settings ++ connect(desktopManger, &DesktopWindowManager::emitFinish, this , [=](){ ++ if (g_emitFinish) ++ return; ++ g_emitFinish = true; ++ GlobalSettings::getInstance()->setDesktopStartUp(false);/* 桌面启动结束 */ ++ QDBusMessage message = QDBusMessage::createMethodCall("org.gnome.SessionManager", ++ "/org/gnome/SessionManager", ++ "org.gnome.SessionManager", ++ "startupfinished"); ++ QList<QVariant> args; ++ args.append("peony-qt-desktop"); ++ args.append("startupfinished"); ++ message.setArguments(args); ++ QDBusConnection::sessionBus().send(message); ++ ++ greatWallDeviceInit(); ++ QtConcurrent::run([=]() { ++ /* 桌面启动之后再挂载本地分区和监听volumes变化 */ ++ autoMountLocalDriver(); ++ monitoringVolumesChanges();//end ++ }); ++ }); ++ + connect(this, &QtSingleApplication::messageReceived, [=](QString msg) { + this->parseCmd(msg, true); + }); +@@ -295,162 +319,96 @@ PeonyDesktopApplication::PeonyDesktopApplication(int &argc, char *argv[], const + connect(trayIcon, &QSystemTrayIcon::messageClicked, trayIcon, &QSystemTrayIcon::hide); + */ + +- // auto mount local driver +- qDebug()<<"auto mount local volumes"; +- GVolumeMonitor* vm = g_volume_monitor_get (); +- if (vm) { +- GList* drives = g_volume_monitor_get_connected_drives(vm); +- if (drives) { +- for (GList* i = drives; nullptr != i; i = i->next) { +- GDrive * d = static_cast<GDrive*>(i->data); +- if (G_IS_DRIVE(d)) { +- GList* volumes = g_drive_get_volumes(d); +- if (volumes) { +- for (GList* j = volumes; nullptr != j; j = j->next) { +- GVolume* v = static_cast<GVolume*>(j->data); +- if (G_IS_VOLUME(v)) { +- g_autofree char* uuid = g_volume_get_uuid(v); +- if (0 != g_strcmp0("2691-6AB8", uuid)) { +- g_volume_mount(v, G_MOUNT_MOUNT_NONE, nullptr, nullptr, volume_mount_cb, nullptr); +- } +- g_object_unref(v); +- } +- } +- g_list_free(volumes); +- } +- g_object_unref(d); +- } +- } +- g_list_free(drives); +- } +- } +- g_object_unref(vm); +- +- g_signal_connect (g_volume_monitor_get(), "mount-added", G_CALLBACK(mount_added_cb), nullptr); +- g_signal_connect (g_volume_monitor_get(), "mount-changed", G_CALLBACK(mount_added_cb), nullptr); ++// if(!GlobalSettings::getInstance()->isDesktopStartUp()){ ++// // auto mount local driver ++// qDebug()<<"auto mount local volumes"; ++// GVolumeMonitor* vm = g_volume_monitor_get (); ++// if (vm) { ++// GList* drives = g_volume_monitor_get_connected_drives(vm); ++// if (drives) { ++// for (GList* i = drives; nullptr != i; i = i->next) { ++// GDrive * d = static_cast<GDrive*>(i->data); ++// if (G_IS_DRIVE(d)) { ++// GList* volumes = g_drive_get_volumes(d); ++// if (volumes) { ++// for (GList* j = volumes; nullptr != j; j = j->next) { ++// GVolume* v = static_cast<GVolume*>(j->data); ++// if (G_IS_VOLUME(v)) { ++// g_autofree char* uuid = g_volume_get_uuid(v); ++// if (0 != g_strcmp0("2691-6AB8", uuid)) { ++// g_volume_mount(v, G_MOUNT_MOUNT_NONE, nullptr, nullptr, volume_mount_cb, nullptr); ++// } ++// g_object_unref(v); ++// } ++// } ++// g_list_free(volumes); ++// } ++// g_object_unref(d); ++// } ++// } ++// g_list_free(drives); ++// } ++// } ++// g_object_unref(vm); ++ ++// g_signal_connect (g_volume_monitor_get(), "mount-added", G_CALLBACK(mount_added_cb), nullptr); ++// g_signal_connect (g_volume_monitor_get(), "mount-changed", G_CALLBACK(mount_added_cb), nullptr); ++// } + + // enumerat network:/// +- QThread* t = QThread::create ([=] () { +- FileEnumerator e; +- e.setEnumerateDirectory ("network:///"); +- e.enumerateSync(); +- e.getChildrenUris (); +- }); +- connect (t, &QThread::finished, t, &QObject::deleteLater); +- t->start (); +- +- } +- +-// connect(this, &SingleApplicatioan::layoutDirectionChanged, this, &PeonyDesktopApplication::layoutDirectionChangedProcess); +-//// connect(this, &SingleApplication::primaryScreenChanged, this, &PeonyDesktopApplication::primaryScreenChangedProcess); +-// connect(this, &SingleApplication::screenAdded, this, &PeonyDesktopApplication::screenAddedProcess); +-// connect(this, &SingleApplication::screenRemoved, this, &PeonyDesktopApplication::screenRemovedProcess); ++ // 适配2503内存优化任务,桌面不拉起gvfsd-dnssd和gvfsd-network ++ // note: 不后台启动可能会导致访问网络变慢,可能需要实际验证是否对用户体验有影响 ++// QThread* t = QThread::create ([=] () { ++// FileEnumerator e; ++// e.setEnumerateDirectory ("network:///"); ++// e.enumerateSync(); ++// e.getChildrenUris (); ++// }); ++// connect (t, &QThread::finished, t, &QObject::deleteLater); ++// t->start (); ++ } ++ ++ connect(this, &SingleApplication::layoutDirectionChanged, this, &PeonyDesktopApplication::layoutDirectionChangedProcess); ++// connect(this, &SingleApplication::primaryScreenChanged, this, &PeonyDesktopApplication::primaryScreenChangedProcess); ++ connect(this, &SingleApplication::screenAdded, this, &PeonyDesktopApplication::screenAddedProcess); ++ connect(this, &SingleApplication::screenRemoved, this, &PeonyDesktopApplication::screenRemovedProcess); + + //parse cmd + qDebug()<<"parse cmd"; + auto message = this->arguments().join(' ').toUtf8(); + parseCmd(message, !isRunning()); + +- //check if is great wall device and init settings +- connect(this, &PeonyDesktopApplication::emitFinish, this , [=](){ +- if (g_emitFinish) +- return; +- g_emitFinish = true; +- QDBusMessage message = QDBusMessage::createMethodCall("org.gnome.SessionManager", +- "/org/gnome/SessionManager", +- "org.gnome.SessionManager", +- "startupfinished"); +- QList<QVariant> args; +- args.append("peony-qt-desktop"); +- args.append("startupfinished"); +- message.setArguments(args); +- QDBusConnection::sessionBus().send(message); +- +- greatWallDeviceInit(); +- }); ++// if(!GlobalSettings::getInstance()->isDesktopStartUp()){ ++// qDebug()<<"monitor volumes change"; ++// auto volumeManager = Peony::VolumeManager::getInstance(); ++// connect(volumeManager,&Peony::VolumeManager::mountAdded,this,[=](const std::shared_ptr<Peony::Mount> &mount){ ++// // auto open dir for inserted dvd. ++// GMount* newMount = (GMount*) g_object_ref(mount->getGMount()); ++// //special Volumn of 839 M upgrade part can not mount ++// if (mount->uuid() != "2691-6AB8") ++// g_mount_guess_content_type(newMount, FALSE, NULL, guessContentTypeCallback, NULL); ++// // mount ++// }); ++// connect(volumeManager,&Peony::VolumeManager::volumeRemoved,this,&PeonyDesktopApplication::volumeRemovedProcess); ++// } + +- qDebug()<<"monitor volumes change"; +- auto volumeManager = Peony::VolumeManager::getInstance(); +- connect(volumeManager,&Peony::VolumeManager::mountAdded,this,[=](const std::shared_ptr<Peony::Mount> &mount){ +- // auto open dir for inserted dvd. +- GMount* newMount = (GMount*) g_object_ref(mount->getGMount()); +- //special Volumn of 839 M upgrade part can not mount +- if (mount->uuid() != "2691-6AB8") +- g_mount_guess_content_type(newMount, FALSE, NULL, guessContentTypeCallback, NULL); +- +- // mount +- }); +- connect(volumeManager,&Peony::VolumeManager::volumeRemoved,this,&PeonyDesktopApplication::volumeRemovedProcess); + // 获取max_size初始值 + //caculateVirtualDesktopGeometry(); + qDebug()<<"peony desktop application constructor end"; + } + +-Peony::DesktopItemModel *PeonyDesktopApplication::getModel() ++Peony::AdvancedDesktopItemModel *PeonyDesktopApplication::getModel() + { + if (!desktop_model) +- desktop_model = new DesktopItemModel(); ++ desktop_model = new AdvancedDesktopItemModel(); + return desktop_model; + } + +-Peony::DesktopIconView *PeonyDesktopApplication::getIconView(QPoint pos) +-{ +- //获取当前屏幕的view,如果是镜像直接返回主屏 +- Peony::DesktopIconView *desktopIconView = getIconView(0); +- QRegion screenRegion(m_config->primaryOutput()->geometry()); +- if (screenRegion.contains(pos)) { +- return desktopIconView; +- }; +- +- for (auto window : m_bg_windows) { +- QRegion screenRegion(window->getLogicalGeometryFromScreen()); +- if (screenRegion.contains(pos)) { +- desktopIconView = window->getIconView(); +- break; +- }; +- } +- return desktopIconView; +-} +- +-Peony::DesktopIconView *PeonyDesktopApplication::getIconView(int id) +-{ +- for (auto window : m_bg_windows) { +- if (id == window->id()) { +- return window->getIconView(); +- } +- } +- return m_bg_windows[0]->getIconView(); +-} +- +-DesktopBackgroundWindow *PeonyDesktopApplication::getWindow(int id) +-{ +- for (auto window : m_bg_windows) { +- if (id == window->id()) { +- return window; +- } +- } +- return m_bg_windows[0]; +-} +- +-Peony::DesktopIconView *PeonyDesktopApplication::getIconView(const KScreen::OutputPtr &output) +-{ +- for (auto window : m_bg_windows) { +- if (output == window->screen()) { +- return window->getIconView(); +- } +- } +- return m_bg_windows[0]->getIconView(); +-} +- +-Peony::DesktopIconView *PeonyDesktopApplication::removeUri(const QString& uri) ++DesktopWindowManager *PeonyDesktopApplication::getDesktopWindowManager() + { +- for (auto window : m_bg_windows) { +- auto view = window->getIconView(); +- if (view->removeItemRect(uri) == 1) { +- return view; +- } +- } +- return m_bg_windows[0]->getIconView(); ++ if (!desktopManger) ++ desktopManger = new DesktopWindowManager(); ++ return desktopManger; + } + + bool PeonyDesktopApplication::userGuideDaemonRunning() +@@ -520,52 +478,6 @@ void PeonyDesktopApplication::gotoSetResolution() + + } + +-void PeonyDesktopApplication::relocateIconView(const KScreen::OutputPtr &output) +-{ +- qInfo()<<"start relocate icon view"; +- //task#74174 更新多屏显示,根据id过滤元素 +- int id = -1; +- DesktopBackgroundWindow *primaryWindow = nullptr; +- for (auto window : m_bg_windows) { +- if (window->screen()->isPrimary()) { +- id = window->id(); +- primaryWindow = window; +- qDebug() << "primary name:" << primaryWindow->screen().data()->name() << " primary id:" << primaryWindow->screen()->id() ; +- break; +- } +- } +- qDebug() << "[PeonyDesktopApplication::relocateIconView] id:" << id; +- if (0 < id) { +- for (auto window : m_bg_windows) { +- if (0 == window->id()) { +- primaryWindow->getIconView()->refreshResolutionChange(); +- window->getIconView()->refreshResolutionChange(); +- window->setId(id); +- primaryWindow->setId(0); +- window->getIconView()->clearItemRect(); +- primaryWindow->getIconView()->clearItemRect(); +- } +- } +- } +- +- for (auto window : m_bg_windows) { +- qDebug() << "screen name :" << window->screen()->name() << " id:" << window->id() ; +- Q_EMIT window->getIconView()->updateView(); +- window->setCentralView(); +- if (!window->screen()->isPrimary() && 1 != m_mode) { +- KWindowSystem::raiseWindow(window->winId()); +- kdk::WindowManager::activateWindow(window->getWindowId()); +- } +- if (0 < id) { +- window->getIconView()->resolutionChange(); +- } +- } +- if(primaryWindow) { +- KWindowSystem::raiseWindow(primaryWindow->winId()); +- kdk::WindowManager::activateWindow(primaryWindow->getWindowId()); +- } +-} +- + void PeonyDesktopApplication::parseCmd(QString msg, bool isPrimary) + { + QCommandLineParser parser; +@@ -585,6 +497,13 @@ void PeonyDesktopApplication::parseCmd(QString msg, bool isPrimary) + QCommandLineOption clearIconsOption(QStringList()<<"c"<<"clear-icons", tr("Clear standard icons")); + parser.addOption(clearIconsOption); + ++ QCommandLineOption layoutOption(QStringList()<<"l"<<"layout-items", tr("Layout item with top to bottom left to right")); ++ parser.addOption(layoutOption); ++ ++ //story28307, 28308, force update background ++ QCommandLineOption UpdateOption(QStringList()<<"u"<<"update-background", tr("Force update backgrounds")); ++ parser.addOption(UpdateOption); ++ + if (isPrimary) { + if (m_first_parse) { + auto helpOption = parser.addHelpOption(); +@@ -597,6 +516,10 @@ void PeonyDesktopApplication::parseCmd(QString msg, bool isPrimary) + parser.process(args); + if (parser.isSet(quitOption)) { + QTimer::singleShot(1000, this, [=]() { ++ if (desktopManger) { ++ delete desktopManger; ++ desktopManger = nullptr; ++ } + qApp->quit(); + }); + return; +@@ -646,6 +569,12 @@ void PeonyDesktopApplication::parseCmd(QString msg, bool isPrimary) + } + + if (parser.isSet(desktopOption)) { ++ if(!has_background) { ++ QDBusServiceWatcher *watcher = new QDBusServiceWatcher(this); ++ watcher->setConnection(QDBusConnection::sessionBus()); ++ watcher->addWatchedService("org.ukui.KWin"); ++ connect(watcher, &QDBusServiceWatcher::serviceRegistered, desktopManger, &DesktopWindowManager::raiseWid); ++ } + setupBgAndDesktop(); + } + +@@ -658,6 +587,14 @@ void PeonyDesktopApplication::parseCmd(QString msg, bool isPrimary) + clearIcons(args); + } + } ++ if (parser.isSet(layoutOption)) { ++ desktopManger->layoutViewItems(); ++ } ++ ++ if (parser.isSet(UpdateOption)) { ++ //force update background use gsettings pictureFile ++ DesktopBackgroundManager::globalInstance()->forceUpdateBackground(); ++ } + + connect(this, &QApplication::paletteChanged, this, [=](const QPalette &pal) { + for (auto w : allWidgets()) { +@@ -698,11 +635,11 @@ void PeonyDesktopApplication::clearIcons(const QStringList &args) + const QString &operationStr = args.value(index); + bool isSuccess = false; + int operationNum = operationStr.toInt(&isSuccess); +- for (auto window : m_bg_windows) { +- if (!window->getIconView()) { +- return; +- } +- } ++// for (auto window : m_bg_windows) { ++// if (!window->getIconView()) { ++// return; ++// } ++// } + if (isSuccess) { + switch (operationNum) { + case 1: +@@ -714,6 +651,58 @@ void PeonyDesktopApplication::clearIcons(const QStringList &args) + } + } + ++void PeonyDesktopApplication::autoMountLocalDriver() ++{ ++ // auto mount local driver ++ qDebug()<<"auto mount local volumes"; ++ GVolumeMonitor* vm = g_volume_monitor_get (); ++ if (vm) { ++ GList* drives = g_volume_monitor_get_connected_drives(vm); ++ if (drives) { ++ for (GList* i = drives; nullptr != i; i = i->next) { ++ GDrive * d = static_cast<GDrive*>(i->data); ++ if (G_IS_DRIVE(d)) { ++ GList* volumes = g_drive_get_volumes(d); ++ if (volumes) { ++ for (GList* j = volumes; nullptr != j; j = j->next) { ++ GVolume* v = static_cast<GVolume*>(j->data); ++ if (G_IS_VOLUME(v)) { ++ g_autofree char* uuid = g_volume_get_uuid(v); ++ if (0 != g_strcmp0("2691-6AB8", uuid)) { ++ g_volume_mount(v, G_MOUNT_MOUNT_NONE, nullptr, nullptr, volume_mount_cb, nullptr); ++ } ++ g_object_unref(v); ++ } ++ } ++ g_list_free(volumes); ++ } ++ g_object_unref(d); ++ } ++ } ++ g_list_free(drives); ++ } ++ } ++ g_object_unref(vm); ++ ++ g_signal_connect (g_volume_monitor_get(), "mount-added", G_CALLBACK(mount_added_cb), nullptr); ++ g_signal_connect (g_volume_monitor_get(), "mount-changed", G_CALLBACK(mount_added_cb), nullptr); ++} ++ ++void PeonyDesktopApplication::monitoringVolumesChanges() ++{ ++ qDebug()<<"monitor volumes change"; ++ auto volumeManager = Peony::VolumeManager::getInstance(); ++ connect(volumeManager,&Peony::VolumeManager::mountAdded,this,[=](const std::shared_ptr<Peony::Mount> &mount){ ++ // auto open dir for inserted dvd. ++ GMount* newMount = (GMount*) g_object_ref(mount->getGMount()); ++ //special Volumn of 839 M upgrade part can not mount ++ if (mount->uuid() != "2691-6AB8") ++ g_mount_guess_content_type(newMount, FALSE, NULL, guessContentTypeCallback, NULL); ++ // mount ++ }); ++ connect(volumeManager,&Peony::VolumeManager::volumeRemoved,this,&PeonyDesktopApplication::volumeRemovedProcess); ++} ++ + void PeonyDesktopApplication::addWindow(QScreen *screen, bool checkPrimay) + { + +@@ -762,118 +751,15 @@ void PeonyDesktopApplication::updateVirtualDesktopGeometryByWindows() + + } + +-void PeonyDesktopApplication::addBgWindow(const KScreen::OutputPtr &output) +-{ +- if (output.isNull() || !output->isEnabled()) { +- return; +- } +- qDebug() << "output added:" << output.data()->name()<< output->id() << output->geometry(); +- int desktopWindowId = getDesktopWindowId(); +- auto window = new DesktopBackgroundWindow(output, desktopWindowId); +- window->getIconView()->refresh(); +- m_bg_windows.append(window); +- desktop_window_id = m_bg_windows.count(); +- qDebug()<<"[PeonyDesktopApplication::addBgWindow] screen name:"<<window->screen()->name()<<" IP:"<<window->screen(); +- window->show(); +- +- connect(window->getIconView(), &DesktopIconView::resetGridSize, this, [=](const QSize &gridSize){ +- for (auto bgWindow : m_bg_windows) { +- bgWindow->getIconView()->setGridSize(gridSize); +- } +- }); +- +- //task#74174 更新图标大小 +- connect(window, &DesktopBackgroundWindow::setDefaultZoomLevel, this, [=](DesktopIconView::ZoomLevel level){ +- for (auto bgWindow : m_bg_windows) { +- if (bgWindow->getIconView()->zoomLevel() != level) { +- bgWindow->getIconView()->setDefaultZoomLevel(level); +- bgWindow->getIconView()->clearExtendItemPos(true); +- } +- } +- }); +- //task#74174 更新排序方式 +- connect(window, &DesktopBackgroundWindow::setSortType, this, [=](int sortType){ +- for (auto bgWindow : m_bg_windows) { +- bgWindow->getIconView()->setSortType(sortType); +- } +- }); +- //task#74174 恢复扩展屏 +- m_mode = checkScreenMode(output->geometry()); +- if (2 == m_mode) { +- multiscreenMode(); +- } +- +- relocateIconView(output); +-} +- +-void PeonyDesktopApplication::outputAdded(const KScreen::OutputPtr &output) +-{ +- for (auto bgWindow : m_bg_windows) { +- if (output == bgWindow->screen()) { +- qWarning("DesktopBackgroundWindow already exists and cannot be added"); +- return; +- } +- } +- if (output.isNull()) { +- return; +- } +- addBgWindow(output); +- connect(output.data(), &KScreen::Output::isEnabledChanged, +- this, [=](){ +- if(output->isEnabled()) { +- QTimer::singleShot(1, output.data() , [=]() { +- addBgWindow(output); +- }); +- } else { +- outputRemoved(output->id()); +- } +- }); +-} +- + void PeonyDesktopApplication::setupDesktop() + { +- connect(kdk::WindowManager::self(),&kdk::WindowManager::windowAdded,this,[=](const kdk::WindowId& windowId){ +- if((quint32)getpid() == kdk::WindowManager::getPid(windowId)) { +- for (auto window : m_bg_windows) { +- QString title = kdk::WindowManager::getWindowTitle(windowId); +- QString windowTitle = window->windowTitle(); +- if (title == windowTitle) { +- window->setWindowId(windowId); +- if (window->screen()->isPrimary()) { +- kdk::WindowManager::activateWindow(window->getWindowId()); +- } +- break; +- } +- } +- } +- }); +- +- KScreen::GetConfigOperation *op = new KScreen::GetConfigOperation(); +- if (op->exec()) { +- setConfig(op); +- } else { +- for (auto screen : qApp->screens()) { +- KScreen::OutputPtr output(new KScreen::Output); +- output->setId(qApp->screens().indexOf(screen)); +- output->setName(qApp->primaryScreen()->name()); +- output->setPos(screen->geometry().topLeft() * screen->devicePixelRatio()); +- output->setExplicitLogicalSize(screen->size() * devicePixelRatio()); +- output->setScale(1.0); +- output->setSize(screen->size() * screen->devicePixelRatio()); +- if (screen == qApp->primaryScreen()) { +- output->setPrimary(true); +- } else { +- output->setPrimary(false); +- } +- output->setConnected(true); +- output->setEnabled(true); +- addBgWindow(output); +- } +- QMessageBox::warning(0, tr("Failed to get screen config"), tr("Error message is: %1. Using fallback config to setup desktop."), op->errorString()); ++ DesktopBackgroundManager::globalInstance(); ++ for (auto screen : qApp->screens()) { ++ desktopManger->addBgWindow(screen); + } +-// connect(op, &KScreen::GetConfigOperation::finished, this, [this](KScreen::ConfigOperation *op) { +-// setConfig(op); +-// }); ++ ++ connect(qApp, &QApplication::screenAdded, desktopManger, &DesktopWindowManager::addBgWindow); ++ connect(this, &PeonyDesktopApplication::primaryScreenChanged, desktopManger, &DesktopWindowManager::relocateIconView); + } + + void PeonyDesktopApplication::setupBgAndDesktop() +@@ -884,62 +770,12 @@ void PeonyDesktopApplication::setupBgAndDesktop() + has_background = true; + } + +-int PeonyDesktopApplication::getDesktopWindowId() +-{ +- int desktopWindowId = 0; +- for (; desktopWindowId <= desktop_window_id; desktopWindowId++) { +- bool find = false; +- for (auto bgWindow : m_bg_windows) { +- if (bgWindow->id() == desktopWindowId) { +- find = true; +- break; +- } +- } +- if (!find) { +- break; +- } +- } +- return desktopWindowId; +-} +- +-void PeonyDesktopApplication::singleScreenMode() +-{ +- auto primayView = getIconView(0); +- qDebug() << "primay view item" << primayView->model()->rowCount() << "total item:" << getModel()->rowCount(); +- if ( 2 == m_mode && primayView->model()->rowCount() == getModel()->rowCount()) { +- primayView->clearExtendItemPos(); +- return; +- } +- for (auto bgWindow : m_bg_windows) { +- auto view = bgWindow->getIconView(); +- view->getAllRestoreInfo(); +- view->clearItemRect(); +- view->saveExtendItemInfo(); +- } +- +- Q_EMIT primayView->updateView(); +-} +- +-void PeonyDesktopApplication::multiscreenMode() +-{ +- for (auto bgWindow : m_bg_windows) { +- auto view = bgWindow->getIconView(); +- view->getAllRestoreInfo(); +- view->clearItemRect(); +- view->resetExtendItemInfo(); +- } +- for (auto bgWindow : m_bg_windows) { +- auto view = bgWindow->getIconView(); +- Q_EMIT view->updateView(); +- } +-} +- + void PeonyDesktopApplication::clearViewCache() + { +- for (auto bgWindow : m_bg_windows) { +- auto view = bgWindow->getIconView(); +- view->clearCache(); +- } ++// for (auto bgWindow : m_bg_windows) { ++// auto view = bgWindow->getIconView(); ++// view->clearCache(); ++// } + } + + void guessContentTypeCallback(GObject* object, GAsyncResult *res,gpointer data) +@@ -958,7 +794,7 @@ void guessContentTypeCallback(GObject* object, GAsyncResult *res,gpointer data) + root = g_mount_get_default_location(G_MOUNT(object)); + + // fix #205338 +- g_autoptr(GFileInfo) access_info = g_file_query_info(root, G_FILE_ATTRIBUTE_ACCESS_CAN_READ","G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, G_FILE_QUERY_INFO_NONE, nullptr, nullptr); ++ g_autoptr(GFileInfo) access_info = g_file_query_info(root, G_FILE_ATTRIBUTE_ACCESS_CAN_READ "," G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, G_FILE_QUERY_INFO_NONE, nullptr, nullptr); + openFolder = g_file_info_get_attribute_boolean(access_info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ) && g_file_info_get_attribute_boolean(access_info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE); + + mountUri = g_file_get_uri(root); +@@ -1054,95 +890,6 @@ void PeonyDesktopApplication::volumeRemovedProcess(const std::shared_ptr<Peony:: + // g_drive_stop(gdrive,G_MOUNT_UNMOUNT_NONE,NULL,NULL,NULL,NULL); + }; + +-int PeonyDesktopApplication::checkScreenMode(const QRect &geometry) +-{ +- int mode = 1; +- if (m_bg_windows.count() == 1) { +- mode = 0; +- return mode; +- } +- +- for (auto window : m_bg_windows) { +- if (window->screen()->geometry() != geometry) { +- mode = 2; +- break; +- } +- } +- return mode; +-} +- +-Peony::DesktopIconView *PeonyDesktopApplication::getNotFullView() +-{ +- for (auto window : m_bg_windows) { +- Peony::DesktopIconView *view = window->getIconView(); +- if (view && !view->isFull()) { +- return view; +- } +- } +- return nullptr; +-} +- +-void PeonyDesktopApplication::setConfig(KScreen::ConfigOperation *op) +-{ +- m_config = op->config(); +- if (!m_config) { +- qCritical("[PeonyDesktopApplication::setConfig] config is null"); +- return; +- } +- KScreen::ConfigMonitor::instance()->addConfig(m_config); +- +- for (const KScreen::OutputPtr &output : m_config->outputs()) { +- outputAdded(output); +- } +- connect(m_config.data(), &KScreen::Config::primaryOutputChanged, +- this, &PeonyDesktopApplication::relocateIconView); +- connect(m_config.data(), &KScreen::Config::outputAdded, +- this, &PeonyDesktopApplication::outputAdded); +- connect(m_config.data(), &KScreen::Config::outputRemoved, +- this, &PeonyDesktopApplication::outputRemoved); +- +-} +- +-void PeonyDesktopApplication::outputRemoved(int outputId) +-{ +- for (auto window : m_bg_windows) { +- if (outputId == window->screen()->id()) { +- if (m_bg_windows.count() - 1 != window->id() && m_bg_windows.count() > 1) { +- auto lastWindow = getWindow(m_bg_windows.count() - 1); +- lastWindow->setId(m_bg_windows.count() - 2); +- window->setId(m_bg_windows.count() - 1); +- lastWindow->getIconView()->updateView(); +- window->getIconView()->updateView(); +- } +- if (m_mode == 2) { +- if (m_bg_windows.count() > 2) { +- //task#74174 销毁时保存扩展屏元素的坐标点 +- window->getIconView()->saveExtendItemInfo(); +- getIconView(0)->updateView(); +- } else if (m_bg_windows.count() == 2) { +- singleScreenMode(); +- m_mode = 0; +- } +- } +- window->invaidScreen(); +- m_bg_windows.removeOne(window); +- window->deleteLater(); +- return; +- } +- } +-} +- +-void PeonyDesktopApplication::changeMode(int mode) +-{ +- if (m_mode != mode) { +- m_mode = mode; +- if (1 == mode) { +- singleScreenMode(); +- } else if (2 == mode) { +- multiscreenMode(); +- } +- } +-} + static void volume_mount_cb (GObject* source, GAsyncResult* res, gpointer udata) + { + g_volume_mount_finish(G_VOLUME (source), res, nullptr); +diff --git a/peony-qt-desktop/peony-desktop-application.h b/peony-qt-desktop/peony-desktop-application.h +index 7483345..7129a22 100644 +--- a/peony-qt-desktop/peony-desktop-application.h ++++ b/peony-qt-desktop/peony-desktop-application.h +@@ -27,17 +27,13 @@ + #include "singleapplication.h" + #include "qtsingleapplication.h" + #include "volume-manager.h" +-#include <KF5/KScreen/kscreen/output.h> +-#include <KF5/KScreen/kscreen/configmonitor.h> +-#include <KF5/KScreen/kscreen/getconfigoperation.h> ++ + #include <QScreen> + #include <QWindow> + +-class DesktopBackgroundWindow; +- ++class DesktopWindowManager; + namespace Peony { +-class DesktopIconView; +-class DesktopItemModel; ++class AdvancedDesktopItemModel; + } + + using namespace Peony; +@@ -56,23 +52,14 @@ public: + static void gotoSetResolution(); + + static qint64 peony_desktop_start_time; +- static Peony::DesktopItemModel* getModel(); +- Peony::DesktopIconView *getIconView(QPoint pos); +- Peony::DesktopIconView *getIconView(int id); +- Peony::DesktopIconView *getIconView(const KScreen::OutputPtr &output); +- DesktopBackgroundWindow *getWindow(int id); +- Peony::DesktopIconView * removeUri(const QString& uri); +- int checkScreenMode(const QRect &geometry); +- Peony::DesktopIconView *getNotFullView(); +- void singleScreenMode(); +- void multiscreenMode(); ++ static Peony::AdvancedDesktopItemModel *getModel(); ++ static DesktopWindowManager *getDesktopWindowManager(); + + // only used in model refresh. + void clearViewCache(); + + Q_SIGNALS: + void requestSetUKUIOutputEnable(bool enable); +- void emitFinish(); + + protected Q_SLOTS: + void parseCmd(QString msg, bool isPrimary); +@@ -90,28 +77,14 @@ public Q_SLOTS: + void checkWindowProcess(); + void updateVirtualDesktopGeometryByWindows(); + +- void addBgWindow(const KScreen::OutputPtr &output); +- void relocateIconView(const KScreen::OutputPtr &output); +- +- void outputAdded(const KScreen::OutputPtr &output); +- void outputRemoved(int outputId); +- void changeMode(int mode); +- + private: + void setupDesktop(); + void setupBgAndDesktop(); +- void setConfig(KScreen::ConfigOperation *op); + void clearIcons(const QStringList &args); +- int getDesktopWindowId(); ++ void autoMountLocalDriver(); ++ void monitoringVolumesChanges(); + + bool m_first_parse = true; +- +- QList<DesktopBackgroundWindow *> m_bg_windows; +- +- QTimeLine *m_primaryScreenSettingsTimeLine = nullptr; +- +- KScreen::ConfigPtr m_config = nullptr; +- int m_mode = 0; + }; + + #endif // PEONYDESKTOPAPPLICATION_H +diff --git a/peony-qt-desktop/peony-qt-desktop.pro b/peony-qt-desktop/peony-qt-desktop.pro +index 349b9fd..43961d4 100644 +--- a/peony-qt-desktop/peony-qt-desktop.pro ++++ b/peony-qt-desktop/peony-qt-desktop.pro +@@ -4,7 +4,7 @@ + # + #------------------------------------------------- + +-QT += core gui x11extras dbus concurrent KWindowSystem KWaylandClient KScreen ++QT += core gui x11extras dbus concurrent KWindowSystem KWaylandClient + + greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +@@ -51,13 +51,14 @@ TRANSLATIONS += ../translations/peony-qt-desktop/peony-qt-desktop_zh_CN.ts \ + ../translations/peony-qt-desktop/peony-qt-desktop_zh_HK.ts + + SOURCES += \ ++ advanced-desktop-icon-view.cpp \ ++ advanced-desktop-item-model.cpp \ + desktop-background-manager.cpp \ ++ desktop-window-manager.cpp \ + desktopbackgroundwindow.cpp \ + main.cpp \ + peony-desktop-application.cpp \ + fm-dbus-service.cpp \ +- desktop-item-model.cpp \ +- desktop-icon-view.cpp \ + desktop-icon-view-delegate.cpp \ + desktop-index-widget.cpp \ + desktop-menu.cpp \ +@@ -73,12 +74,14 @@ SOURCES += \ + ukui-output-core.c + + HEADERS += \ ++ advanced-desktop-icon-view.h \ ++ advanced-desktop-item-model.h \ ++ common.h \ + desktop-background-manager.h \ ++ desktop-window-manager.h \ + desktopbackgroundwindow.h \ + peony-desktop-application.h \ + fm-dbus-service.h \ +- desktop-item-model.h \ +- desktop-icon-view.h \ + desktop-icon-view-delegate.h \ + desktop-index-widget.h \ + desktop-menu.h \ +diff --git a/peony-qt-desktop/user-dir-manager.cpp b/peony-qt-desktop/user-dir-manager.cpp +index f11fa07..b287da1 100644 +--- a/peony-qt-desktop/user-dir-manager.cpp ++++ b/peony-qt-desktop/user-dir-manager.cpp +@@ -46,7 +46,6 @@ UserdirManager::UserdirManager(QObject *parent) : QObject(parent) + m_user_name = QString(userName); + + m_settings = new QSettings("org.ukui", "peony-qt-preferences", this); +- m_do_not_thumbnail = m_settings->value(FORBID_THUMBNAIL_IN_VIEW).toBool(); + m_allow_parallel = m_settings->value(ALLOW_FILE_OP_PARALLEL).toBool(); + m_showTrashDialog = m_settings->value(SHOW_TRASH_DIALOG).toBool(); + m_user_dir_watcher = new QFileSystemWatcher(this); +@@ -101,11 +100,6 @@ UserdirManager::UserdirManager(QObject *parent) : QObject(parent) + else if(uri == path1) + { + m_settings = new QSettings("org.ukui", "peony-qt-preferences", this); +- if(m_do_not_thumbnail != m_settings->value(FORBID_THUMBNAIL_IN_VIEW).toBool()) +- { +- m_do_not_thumbnail = m_settings->value(FORBID_THUMBNAIL_IN_VIEW).toBool(); +- Q_EMIT thumbnailSetingChange(); +- } + if (m_allow_parallel != m_settings->value(ALLOW_FILE_OP_PARALLEL).toBool()) + { + m_allow_parallel = m_settings->value(ALLOW_FILE_OP_PARALLEL).toBool(); +@@ -121,6 +115,18 @@ UserdirManager::UserdirManager(QObject *parent) : QObject(parent) + m_user_dir_watcher->addPath(uri); + }); + ++ if (GlobalSettings::getInstance()->isExist(FORBID_THUMBNAIL_IN_VIEW)) { ++ m_do_not_thumbnail = GlobalSettings::getInstance()->getValue(FORBID_THUMBNAIL_IN_VIEW).toBool(); ++ connect(GlobalSettings::getInstance(), &GlobalSettings::valueChanged, this, [=] (const QString& key) { ++ if (FORBID_THUMBNAIL_IN_VIEW == key) { ++ if(m_do_not_thumbnail != GlobalSettings::getInstance()->getValue(FORBID_THUMBNAIL_IN_VIEW).toBool()) ++ { ++ m_do_not_thumbnail = !m_do_not_thumbnail; ++ Q_EMIT thumbnailSetingChange(); ++ } ++ } ++ }); ++ } + } + // read user-dirs.dirs for all XDG standard path. + void UserdirManager::getUserdir() +diff --git a/peony-qt-desktop/user-dir-manager.h b/peony-qt-desktop/user-dir-manager.h +index da68a37..cf5d501 100644 +--- a/peony-qt-desktop/user-dir-manager.h ++++ b/peony-qt-desktop/user-dir-manager.h +@@ -52,7 +52,7 @@ private: + QDir *m_dir; + QStringList m_file_list; + QSettings *m_settings; +- bool m_do_not_thumbnail; ++ bool m_do_not_thumbnail = false; + bool m_allow_parallel; + bool m_showTrashDialog; + int m_times = 9; +diff --git a/translations/peony-qt-desktop/peony-qt-desktop_zh_CN.ts b/translations/peony-qt-desktop/peony-qt-desktop_zh_CN.ts +index fd82304..1c6551b 100644 +--- a/translations/peony-qt-desktop/peony-qt-desktop_zh_CN.ts ++++ b/translations/peony-qt-desktop/peony-qt-desktop_zh_CN.ts +@@ -2,16 +2,47 @@ + <!DOCTYPE TS> + <TS version="2.1" language="zh_CN"> + <context> +- <name>Peony::DesktopIconView</name> ++ <name>AdvancedDesktopIconView</name> ++ <message> ++ <location filename="../../peony-qt-desktop/advanced-desktop-icon-view.cpp" line="465"/> ++ <source>New Folder</source> ++ <translation>新建文件夹</translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/advanced-desktop-icon-view.cpp" line="572"/> ++ <source>Open Link failed</source> ++ <translation>打开快捷方式失败</translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/advanced-desktop-icon-view.cpp" line="573"/> ++ <source>File not exist, do you want to delete the link file?</source> ++ <translation>目标文件夹不存在,是否删除该无效快捷方式?</translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/advanced-desktop-icon-view.cpp" line="589"/> ++ <source>Open failed</source> ++ <translation>打开失败</translation> ++ </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-icon-view.h" line="89"/> ++ <location filename="../../peony-qt-desktop/advanced-desktop-icon-view.cpp" line="590"/> ++ <source>Open directory failed, you have no permission!</source> ++ <translation>打开文件夹失败,您没有该目录的权限!</translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/advanced-desktop-icon-view.h" line="101"/> + <source>Desktop Icon View</source> + <translation>桌面图标视图</translation> + </message> ++</context> ++<context> ++ <name>Peony::DesktopIconView</name> ++ <message> ++ <source>Desktop Icon View</source> ++ <translation type="vanished">桌面图标视图</translation> ++ </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="674"/> + <source>New Folder</source> +- <translation>新建文件夹</translation> ++ <translation type="vanished">新建文件夹</translation> + </message> + <message> + <source>set background</source> +@@ -22,29 +53,24 @@ + <translation type="vanished">删除文件警告</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="1036"/> + <source>Open failed</source> +- <translation>打开失败</translation> ++ <translation type="vanished">打开失败</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="1037"/> + <source>Open directory failed, you have no permission!</source> +- <translation>打开文件夹失败,您没有该目录的权限!</translation> ++ <translation type="vanished">打开文件夹失败,您没有该目录的权限!</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="1019"/> + <source>Open Link failed</source> +- <translation>打开快捷方式失败</translation> ++ <translation type="vanished">打开快捷方式失败</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="768"/> + <source>Set Background</source> +- <translation>设置背景</translation> ++ <translation type="vanished">设置背景</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-icon-view.cpp" line="1020"/> + <source>File not exist, do you want to delete the link file?</source> +- <translation>目标文件夹不存在,是否删除该无效快捷方式?</translation> ++ <translation type="vanished">目标文件夹不存在,是否删除该无效快捷方式?</translation> + </message> + </context> + <context> +@@ -101,7 +127,7 @@ + <translation type="vanished">打开%1个选中文件(&O)</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="237"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="238"/> + <source>Reverse Select</source> + <translation>反选</translation> + </message> +@@ -122,7 +148,7 @@ + <translation type="vanished">文件夹(&F)</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="355"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="359"/> + <source>New Folder</source> + <translation>新建文件夹</translation> + </message> +@@ -147,20 +173,20 @@ + <translation type="vanished">超大图标(&H)</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="109"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="111"/> + <source>Open in new Window</source> + <translation>在新窗口中打开</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="114"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="116"/> + <source>Select All</source> + <translation>全选</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="137"/> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="165"/> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="199"/> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="210"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="139"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="167"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="201"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="212"/> + <source>Open</source> + <translation>打开</translation> + </message> +@@ -169,64 +195,69 @@ + <translation type="vanished">无法打开路径 "%1", 权限被拒绝。</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="145"/> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="170"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="147"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="172"/> + <source>Open with...</source> + <translation>打开方式</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="158"/> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="193"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="160"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="195"/> + <source>More applications...</source> + <translation>更多应用</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="213"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="215"/> + <source>Open %1 selected files</source> + <translation>打开%1个选中文件</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="251"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="252"/> + <source>New</source> + <translation>新建</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="339"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="341"/> + <source>Empty File</source> + <translation>空文本</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="351"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="354"/> + <source>Folder</source> + <translation>文件夹</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="374"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="378"/> ++ <source>Auto arrange</source> ++ <translation>自动排列</translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="387"/> + <source>View Type</source> + <translation>视图类型</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="380"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="393"/> + <source>Small</source> + <translation>小图标</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="383"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="396"/> + <source>Normal</source> + <translation>中图标</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="386"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="399"/> + <source>Large</source> + <translation>大图标</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="389"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="402"/> + <source>Huge</source> + <translation>超大图标</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="417"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="430"/> + <source>Sort By</source> + <translation>排序方式</translation> + </message> +@@ -235,32 +266,37 @@ + <translation type="vanished">排序方式...</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="422"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="435"/> + <source>Name</source> + <translation>文件名称</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="424"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="437"/> + <source>File Type</source> + <translation>文件类型</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="425"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="438"/> + <source>File Size</source> + <translation>文件大小</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="497"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="460"/> ++ <source>Sort Order</source> ++ <translation>排序顺序</translation> ++ </message> ++ <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="510"/> + <source>Clean the trash</source> + <translation>清空回收站</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="518"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="531"/> + <source>Copy</source> + <translation>复制</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="545"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="558"/> + <source>Delete to trash</source> + <translation>删除到回收站</translation> + </message> +@@ -273,7 +309,7 @@ + <translation type="vanished">不能将回收站与其他文件一起查看属性!</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="423"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="436"/> + <source>Modified Date</source> + <translation>修改日期</translation> + </message> +@@ -282,7 +318,7 @@ + <translation type="vanished">删除文件警告</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="524"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="537"/> + <source>Cut</source> + <translation>剪切</translation> + </message> +@@ -291,28 +327,28 @@ + <translation type="vanished">删除到回收站(&D)</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="554"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="567"/> + <source>Delete forever</source> + <translation>永久删除</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="562"/> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="567"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="575"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="580"/> + <source>Rename</source> + <translation>重命名</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="574"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="587"/> + <source>Paste</source> + <translation>粘贴</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="580"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="593"/> + <source>Refresh</source> + <translation>刷新</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="594"/> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="610"/> + <source>Properties</source> + <translation>属性</translation> + </message> +@@ -325,12 +361,14 @@ + <translation type="vanished">排列顺序...</translation> + </message> + <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="465"/> + <source>Ascending Order</source> +- <translation type="vanished">升序</translation> ++ <translation>升序</translation> + </message> + <message> ++ <location filename="../../peony-qt-desktop/desktop-menu.cpp" line="464"/> + <source>Descending Order</source> +- <translation type="vanished">降序</translation> ++ <translation>降序</translation> + </message> + <message> + <source>Zoom &In</source> +@@ -433,7 +471,7 @@ + <translation type="vanished">关闭桌面并退出</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="229"/> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="227"/> + <source>peony-qt-desktop</source> + <translation>桌面</translation> + </message> +@@ -446,39 +484,39 @@ + <translation type="vanished">桌面</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="573"/> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="485"/> + <source>Close the peony desktop window</source> + <translation>关闭桌面程序</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="576"/> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="488"/> + <source>Take over the dbus service.</source> + <translation>接管DBus服务。</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="579"/> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="491"/> + <source>Take over the desktop displaying</source> + <translation>接管桌面</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="582"/> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="494"/> + <source>Setup backgrounds</source> + <translation>Setup backgrounds</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="585"/> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="497"/> + <source>Clear standard icons</source> + <translation>Clear standard icons</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="872"/> +- <source>Failed to get screen config</source> +- <translation type="unfinished"></translation> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="500"/> ++ <source>Layout item with top to bottom left to right</source> ++ <translation>从左上到右下排列图标</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="872"/> +- <source>Error message is: %1. Using fallback config to setup desktop.</source> +- <translation type="unfinished"></translation> ++ <location filename="../../peony-qt-desktop/peony-desktop-application.cpp" line="504"/> ++ <source>Force update backgrounds</source> ++ <translation>强制更新背景</translation> + </message> + <message> + <source>Open learning center.</source> +@@ -508,12 +546,12 @@ + <translation type="vanished">错误</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktopbackgroundwindow.cpp" line="105"/> ++ <location filename="../../peony-qt-desktop/desktopbackgroundwindow.cpp" line="111"/> + <source>Set Background</source> + <translation>设置背景</translation> + </message> + <message> +- <location filename="../../peony-qt-desktop/desktopbackgroundwindow.cpp" line="110"/> ++ <location filename="../../peony-qt-desktop/desktopbackgroundwindow.cpp" line="116"/> + <source>Display Settings</source> + <translation>显示设置</translation> + </message> diff -Nru peony-4.10.0.5/debian/patches/0092-QGSettings-key.patch peony-4.10.0.5/debian/patches/0092-QGSettings-key.patch --- peony-4.10.0.5/debian/patches/0092-QGSettings-key.patch 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/patches/0092-QGSettings-key.patch 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,23 @@ +From: yanglg1 <yangling@kylinos.cn> +Date: Fri, 28 Feb 2025 16:24:27 +0800 +Subject: =?utf-8?b?5L+u5pS5UUdTZXR0aW5nc+eahGtleeWAvOS4jeWtmOWcqOmXrumimA==?= + +--- + peony-qt-desktop/advanced-desktop-icon-view.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/peony-qt-desktop/advanced-desktop-icon-view.cpp b/peony-qt-desktop/advanced-desktop-icon-view.cpp +index f505d1a..3caf402 100644 +--- a/peony-qt-desktop/advanced-desktop-icon-view.cpp ++++ b/peony-qt-desktop/advanced-desktop-icon-view.cpp +@@ -648,8 +648,8 @@ void AdvancedDesktopIconView::initDoubleClick() + + void AdvancedDesktopIconView::setMargins() + { +- int settingsislandposition = m_panelSetting->get("settingsislandposition").toInt(); +- int paneltype = m_panelSetting->get("paneltype").toInt(); ++ int settingsislandposition = m_panelSetting->keys().contains("settingsislandposition") ? m_panelSetting->get("settingsislandposition").toInt() : -1; ++ int paneltype = m_panelSetting->keys().contains("paneltype") ? m_panelSetting->get("paneltype").toInt() : -1; + int position = m_panelSetting->get("panelposition").toInt(); + int margins = m_panelSetting->get("panelsize").toInt(); + diff -Nru peony-4.10.0.5/debian/patches/0093-Merge-branch-openkylin-nile-of-gitee.com-openkylin-p.patch peony-4.10.0.5/debian/patches/0093-Merge-branch-openkylin-nile-of-gitee.com-openkylin-p.patch --- peony-4.10.0.5/debian/patches/0093-Merge-branch-openkylin-nile-of-gitee.com-openkylin-p.patch 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/patches/0093-Merge-branch-openkylin-nile-of-gitee.com-openkylin-p.patch 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,29 @@ +From: RenYangguang <14705561+renyangguang@user.noreply.gitee.com> +Date: Tue, 11 Mar 2025 03:23:40 +0000 +Subject: Merge branch 'openkylin/nile' of gitee.com:openkylin/peony into + hotfix-bug#IB73X8-sidebar-personal-err + +Signed-off-by: RenYangguang <14705561+renyangguang@user.noreply.gitee.com> +--- + src/control/navigation-side-bar.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/control/navigation-side-bar.cpp b/src/control/navigation-side-bar.cpp +index 317c1b1..d6c70eb 100644 +--- a/src/control/navigation-side-bar.cpp ++++ b/src/control/navigation-side-bar.cpp +@@ -483,12 +483,12 @@ NavigationSideBar::NavigationSideBar(QWidget *parent) : QTreeView(parent) + } + + /** +- * @bug #278107: [Requirement 35207] [Start Menu] [Ribbon] Shortcut Entry-Computer Jump to the left side of the computer interface positioning error ++ * @bug #IB73X8: 【次要】【文件管理器】双击桌面计算机图标,打开的文官左侧选择栏高亮栏为个人栏,应不显示高亮 + * + * comment the following code + * + * @author: Renyg <renyangguang@kylinos.cn> +- * @date: 2024-11-01 ++ * @date: 2025-02-24 + */ + #if 0 + /* 打开文件管理器默认聚焦在家目录上 */ diff -Nru peony-4.10.0.5/debian/patches/series peony-4.10.0.5/debian/patches/series --- peony-4.10.0.5/debian/patches/series 2024-12-24 18:28:47.000000000 +0800 +++ peony-4.10.0.5/debian/patches/series 2025-03-03 09:26:07.000000000 +0800 @@ -84,3 +84,10 @@ 0084-169-hotfix-issues-I9V8VT.patch 0085-bugfix-I9VP8N.patch 0086-bugfix-I9VA55.patch +0087-Added-translation-using-Weblate-Arabic.patch +0088-Added-translation-using-Weblate-Arabic.patch +0089-Added-translation-using-Weblate-Vietnamese.patch +0090-.patch +0091-update-changelog.patch +0092-QGSettings-key.patch +0093-Merge-branch-openkylin-nile-of-gitee.com-openkylin-p.patch diff -Nru peony-4.10.0.5/debian/peony-utils.install peony-4.10.0.5/debian/peony-utils.install --- peony-4.10.0.5/debian/peony-utils.install 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/peony-utils.install 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,4 @@ +debian/upgrade-utils/desktop-grid/meta-get usr/bin +debian/upgrade-utils/desktop-grid/meta-set usr/bin +debian/upgrade-utils/desktop-grid/update-desktop-grid usr/bin +debian/upgrade-utils/desktop-grid/config-parser/paser/meta-conf-parser usr/bin diff -Nru peony-4.10.0.5/debian/peony-utils.manpages peony-4.10.0.5/debian/peony-utils.manpages --- peony-4.10.0.5/debian/peony-utils.manpages 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/peony-utils.manpages 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1 @@ +debian/upgrade-utils/desktop-grid/*.1 diff -Nru peony-4.10.0.5/debian/peony-utils.postinst peony-4.10.0.5/debian/peony-utils.postinst --- peony-4.10.0.5/debian/peony-utils.postinst 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/peony-utils.postinst 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,7 @@ +#!/bin/sh + +set -e + +update-desktop-grid + +#DEBHELPER# diff -Nru peony-4.10.0.5/debian/rules peony-4.10.0.5/debian/rules --- peony-4.10.0.5/debian/rules 2024-12-24 18:28:47.000000000 +0800 +++ peony-4.10.0.5/debian/rules 2025-03-03 09:26:07.000000000 +0800 @@ -5,3 +5,16 @@ %: dh $@ + +override_dh_auto_build: + echo $(CXXFLAGS) + g++ -o $(CURDIR)/debian/upgrade-utils/desktop-grid/config-parser/paser/meta-conf-parser -DQT_NO_DEBUG_OUTPUT -fPIC $(CXXFLAGS) $(CURDIR)/debian/upgrade-utils/desktop-grid/config-parser/paser/main.cpp `pkg-config --cflags --libs Qt5Core gio-2.0` + gcc -o $(CURDIR)/debian/upgrade-utils/desktop-grid/meta-get $(CFLAGS) -I$(CURDIR)/debian/upgrade-utils/desktop-grid $(CURDIR)/debian/upgrade-utils/desktop-grid/meta-get.c $(CURDIR)/debian/upgrade-utils/desktop-grid/metatree.c $(CURDIR)/debian/upgrade-utils/desktop-grid/metabuilder.c $(CURDIR)/debian/upgrade-utils/desktop-grid/crc32.c `pkg-config --cflags --libs gio-2.0` + gcc -o $(CURDIR)/debian/upgrade-utils/desktop-grid/meta-set $(CFLAGS) -I$(CURDIR)/debian/upgrade-utils/desktop-grid $(CURDIR)/debian/upgrade-utils/desktop-grid/meta-set.c $(CURDIR)/debian/upgrade-utils/desktop-grid/metatree.c $(CURDIR)/debian/upgrade-utils/desktop-grid/metabuilder.c $(CURDIR)/debian/upgrade-utils/desktop-grid/crc32.c `pkg-config --cflags --libs gio-2.0` + dh_auto_build + +override_dh_clean: + dh_clean + rm -f $(CURDIR)/debian/upgrade-utils/desktop-grid/meta-get + rm -f $(CURDIR)/debian/upgrade-utils/desktop-grid/meta-set + rm -f $(CURDIR)/debian/upgrade-utils/desktop-grid/config-parser/paser/meta-conf-parser diff -Nru peony-4.10.0.5/debian/upgrade-utils/desktop-grid/Makefile peony-4.10.0.5/debian/upgrade-utils/desktop-grid/Makefile --- peony-4.10.0.5/debian/upgrade-utils/desktop-grid/Makefile 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/upgrade-utils/desktop-grid/Makefile 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,7 @@ +out: + gcc -o meta-get -I./ meta-get.c metatree.c metabuilder.c crc32.c `pkg-config --cflags --libs gio-2.0` + gcc -o meta-set -I./ meta-set.c metatree.c metabuilder.c crc32.c `pkg-config --cflags --libs gio-2.0` + +clean: + rm meta-get + rm meta-set diff -Nru peony-4.10.0.5/debian/upgrade-utils/desktop-grid/config-parser/paser/main.cpp peony-4.10.0.5/debian/upgrade-utils/desktop-grid/config-parser/paser/main.cpp --- peony-4.10.0.5/debian/upgrade-utils/desktop-grid/config-parser/paser/main.cpp 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/upgrade-utils/desktop-grid/config-parser/paser/main.cpp 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,137 @@ +#include <QSettings> +#include <QStandardPaths> +#include <QPoint> +#include <QSize> +#include <QProcess> +#include <QDebug> + +#undef signals + +#include <gio/gio.h> + +QHash <QString, QPoint> metaPoses; +QHash <QString, QPoint> gridPoses; +QList <QPoint> points; + +void updateMetaInfoGridConfig() +{ + QProcess p; + p.setProgram("/usr/bin/meta-set"); + for (auto item : gridPoses.keys()) { + auto gridPos = gridPoses.value(item); + if (item.startsWith("/")) { + p.setArguments(QStringList()<<"-l"<<item<<"peony-qt-desktop-item-grid-pos"<<QString::number(gridPos.x())<<QString::number(gridPos.y())); + p.start(); + p.waitForFinished(); + } else if (item.startsWith("computer")) { + p.setArguments(QStringList()<<"-tl"<<"computer:"<<"/"<<"peony-qt-desktop-item-grid-pos"<<QString::number(gridPos.x())<<QString::number(gridPos.y())); + p.start(); + p.waitForFinished(); + } else if (item.startsWith("trash")) { + p.setArguments(QStringList()<<"-tl"<<"trash:"<<"/"<<"peony-qt-desktop-item-grid-pos"<<QString::number(gridPos.x())<<QString::number(gridPos.y())); + p.start(); + p.waitForFinished(); + } + } + p.close(); +} + +int main(int argc, char *argv[]) +{ + Q_UNUSED(argc) + Q_UNUSED(argv) + //QCoreApplication a(argc, argv); + + QString configPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/.peony-desktop-grid.conf"; + auto keyfile = g_key_file_new(); + // 待更新的groups + QStringList groupsList; + if (g_key_file_load_from_file(keyfile, configPath.toUtf8().constData(), G_KEY_FILE_NONE, nullptr)) { + gchar **groups = g_key_file_get_groups(keyfile, nullptr); + gchar **p = groups; + while (*p) { + int x = g_key_file_get_integer(keyfile, *p, "x", nullptr); + int y = g_key_file_get_integer(keyfile, *p, "y", nullptr); + //qDebug()<<"group"<<*p<<"x"<<x<<"y"<<y; + + QPoint pos = QPoint(x, y); + metaPoses.insert(*p, pos); + if (!pos.isNull()) { + groupsList << *p; + if (!points.contains(pos)) { + points.append(pos); + } + } else { + // 按照未分配的网格设置(-1, -1) + g_key_file_set_integer(keyfile, *p, "gridX", -1); + g_key_file_set_integer(keyfile, *p, "gridY", -1); + gridPoses.insert(*p, QPoint(-1, -1)); + } + p++; + } + if (groups) + g_strfreev (groups); + //qDebug()<<groupsList; + } else { + // 存在问题 + return -1; + } + + // 尝试读取配置 + // 读取当前peony的网格配置(2303以后的版本有网格配置,作为网格大小),如果不存在此配置,则使用网格算法1对网格进行计算后排列 + QSize gridSize; + QSettings peonyCurrentSettings(QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/.config/org.ukui/peony-qt-preferences.conf", QSettings::NativeFormat); + if (peonyCurrentSettings.childKeys().contains("default-grid-size")) { + gridSize = peonyCurrentSettings.value("default-grid-size").toSize(); + qDebug()<<"read config"<<gridSize; + } + + // 尝试计算网格 + // 网格算法1,排除在0,0的点(0,0一般是桌面满屏时放置的点),点任取一点,和其它点作差,得到和其它点x、y的差值,最小的值为网格的width、height,如果存在差值为0也排除(多屏场景可能有图标的位置相同)。此算法较为简单,但是对于图标比较少和分散或者存在异常图标位置的场景处理较差 + // 基于网格算法1,如果存在异常点,则需要加入验算流程,如果无法正常处理或者网格大小偏差太大,则抛弃网格算法1 + if (gridSize.isEmpty() && points.count() > 1) { + auto anchorPoint = points.takeFirst(); + int gridWidth = INT_MAX; + int gridHeigt = INT_MAX; + while (!points.isEmpty()) { + auto currentPoint = points.takeFirst(); + QPoint offset = anchorPoint - currentPoint; + int offsetX = qAbs(offset.x()); + int offsetY = qAbs(offset.y()); + if (offsetX > 0) { + gridWidth = qMin(offsetX, gridWidth); + } + if (offsetY > 0) { + gridHeigt = qMin(offsetY, gridHeigt); + } + } + } + qDebug()<<"grid size"<<gridSize; + + // 无法计算网格的情况下,根据当前图标大小,给定一个合适的网格,根据现有位置顺序对图标进行重排 + + // 按照计算的网格大小更新各个item的网格位置 + for (QString group : groupsList) { + if (gridPoses.contains(group)) { + g_key_file_set_integer(keyfile, group.toUtf8().constData(), "gridX", -1); + g_key_file_set_integer(keyfile, group.toUtf8().constData(), "gridY", -1); + continue; + } + auto pos = metaPoses.value(group); + int gridX = pos.x()/gridSize.width(); + int gridY = pos.y()/gridSize.height(); + g_key_file_set_integer(keyfile, group.toUtf8().constData(), "gridX", gridX); + g_key_file_set_integer(keyfile, group.toUtf8().constData(), "gridY", gridY); + gridPoses.insert(group, QPoint(gridX, gridY)); + } + + g_key_file_save_to_file(keyfile, configPath.toUtf8().constData(), nullptr); + g_key_file_free (keyfile); + + qDebug()<<metaPoses; + qDebug()<<gridPoses; + + updateMetaInfoGridConfig(); + + return 0; +} diff -Nru peony-4.10.0.5/debian/upgrade-utils/desktop-grid/config-parser/paser/paser.pro peony-4.10.0.5/debian/upgrade-utils/desktop-grid/config-parser/paser/paser.pro --- peony-4.10.0.5/debian/upgrade-utils/desktop-grid/config-parser/paser/paser.pro 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/upgrade-utils/desktop-grid/config-parser/paser/paser.pro 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,19 @@ +QT -= gui + +CONFIG += c++11 console link_pkgconfig no_keywords +CONFIG -= app_bundle +PKGCONFIG += gio-2.0 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + main.cpp diff -Nru peony-4.10.0.5/debian/upgrade-utils/desktop-grid/crc32.c peony-4.10.0.5/debian/upgrade-utils/desktop-grid/crc32.c --- peony-4.10.0.5/debian/upgrade-utils/desktop-grid/crc32.c 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/upgrade-utils/desktop-grid/crc32.c 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,95 @@ +/* + * Copyright � 2002, 2003 Sun Microsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Sun Microsystems, Inc. nor the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES OR + * LIABILITIES SUFFERED BY LICENSEE AS A RESULT OF OR RELATING TO USE, + * MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR ITS DERIVATIVES. + * IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, + * PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, + * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE + * THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE + * SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +/* $Id$ */ +/* @(#)crc32.c 1.5 03/01/08 SMI */ + +/* + * + * @file crc32.c + * @brief CRC-32 calculation function + * @author Alexander Gelfenbain + * + */ + +#include "crc32.h" + +static const guint32 crcTable[256] = { + 0x00000000UL, 0x77073096UL, 0xEE0E612CUL, 0x990951BAUL, 0x076DC419UL, 0x706AF48FUL, 0xE963A535UL, 0x9E6495A3UL, + 0x0EDB8832UL, 0x79DCB8A4UL, 0xE0D5E91EUL, 0x97D2D988UL, 0x09B64C2BUL, 0x7EB17CBDUL, 0xE7B82D07UL, 0x90BF1D91UL, + 0x1DB71064UL, 0x6AB020F2UL, 0xF3B97148UL, 0x84BE41DEUL, 0x1ADAD47DUL, 0x6DDDE4EBUL, 0xF4D4B551UL, 0x83D385C7UL, + 0x136C9856UL, 0x646BA8C0UL, 0xFD62F97AUL, 0x8A65C9ECUL, 0x14015C4FUL, 0x63066CD9UL, 0xFA0F3D63UL, 0x8D080DF5UL, + 0x3B6E20C8UL, 0x4C69105EUL, 0xD56041E4UL, 0xA2677172UL, 0x3C03E4D1UL, 0x4B04D447UL, 0xD20D85FDUL, 0xA50AB56BUL, + 0x35B5A8FAUL, 0x42B2986CUL, 0xDBBBC9D6UL, 0xACBCF940UL, 0x32D86CE3UL, 0x45DF5C75UL, 0xDCD60DCFUL, 0xABD13D59UL, + 0x26D930ACUL, 0x51DE003AUL, 0xC8D75180UL, 0xBFD06116UL, 0x21B4F4B5UL, 0x56B3C423UL, 0xCFBA9599UL, 0xB8BDA50FUL, + 0x2802B89EUL, 0x5F058808UL, 0xC60CD9B2UL, 0xB10BE924UL, 0x2F6F7C87UL, 0x58684C11UL, 0xC1611DABUL, 0xB6662D3DUL, + 0x76DC4190UL, 0x01DB7106UL, 0x98D220BCUL, 0xEFD5102AUL, 0x71B18589UL, 0x06B6B51FUL, 0x9FBFE4A5UL, 0xE8B8D433UL, + 0x7807C9A2UL, 0x0F00F934UL, 0x9609A88EUL, 0xE10E9818UL, 0x7F6A0DBBUL, 0x086D3D2DUL, 0x91646C97UL, 0xE6635C01UL, + 0x6B6B51F4UL, 0x1C6C6162UL, 0x856530D8UL, 0xF262004EUL, 0x6C0695EDUL, 0x1B01A57BUL, 0x8208F4C1UL, 0xF50FC457UL, + 0x65B0D9C6UL, 0x12B7E950UL, 0x8BBEB8EAUL, 0xFCB9887CUL, 0x62DD1DDFUL, 0x15DA2D49UL, 0x8CD37CF3UL, 0xFBD44C65UL, + 0x4DB26158UL, 0x3AB551CEUL, 0xA3BC0074UL, 0xD4BB30E2UL, 0x4ADFA541UL, 0x3DD895D7UL, 0xA4D1C46DUL, 0xD3D6F4FBUL, + 0x4369E96AUL, 0x346ED9FCUL, 0xAD678846UL, 0xDA60B8D0UL, 0x44042D73UL, 0x33031DE5UL, 0xAA0A4C5FUL, 0xDD0D7CC9UL, + 0x5005713CUL, 0x270241AAUL, 0xBE0B1010UL, 0xC90C2086UL, 0x5768B525UL, 0x206F85B3UL, 0xB966D409UL, 0xCE61E49FUL, + 0x5EDEF90EUL, 0x29D9C998UL, 0xB0D09822UL, 0xC7D7A8B4UL, 0x59B33D17UL, 0x2EB40D81UL, 0xB7BD5C3BUL, 0xC0BA6CADUL, + 0xEDB88320UL, 0x9ABFB3B6UL, 0x03B6E20CUL, 0x74B1D29AUL, 0xEAD54739UL, 0x9DD277AFUL, 0x04DB2615UL, 0x73DC1683UL, + 0xE3630B12UL, 0x94643B84UL, 0x0D6D6A3EUL, 0x7A6A5AA8UL, 0xE40ECF0BUL, 0x9309FF9DUL, 0x0A00AE27UL, 0x7D079EB1UL, + 0xF00F9344UL, 0x8708A3D2UL, 0x1E01F268UL, 0x6906C2FEUL, 0xF762575DUL, 0x806567CBUL, 0x196C3671UL, 0x6E6B06E7UL, + 0xFED41B76UL, 0x89D32BE0UL, 0x10DA7A5AUL, 0x67DD4ACCUL, 0xF9B9DF6FUL, 0x8EBEEFF9UL, 0x17B7BE43UL, 0x60B08ED5UL, + 0xD6D6A3E8UL, 0xA1D1937EUL, 0x38D8C2C4UL, 0x4FDFF252UL, 0xD1BB67F1UL, 0xA6BC5767UL, 0x3FB506DDUL, 0x48B2364BUL, + 0xD80D2BDAUL, 0xAF0A1B4CUL, 0x36034AF6UL, 0x41047A60UL, 0xDF60EFC3UL, 0xA867DF55UL, 0x316E8EEFUL, 0x4669BE79UL, + 0xCB61B38CUL, 0xBC66831AUL, 0x256FD2A0UL, 0x5268E236UL, 0xCC0C7795UL, 0xBB0B4703UL, 0x220216B9UL, 0x5505262FUL, + 0xC5BA3BBEUL, 0xB2BD0B28UL, 0x2BB45A92UL, 0x5CB36A04UL, 0xC2D7FFA7UL, 0xB5D0CF31UL, 0x2CD99E8BUL, 0x5BDEAE1DUL, + 0x9B64C2B0UL, 0xEC63F226UL, 0x756AA39CUL, 0x026D930AUL, 0x9C0906A9UL, 0xEB0E363FUL, 0x72076785UL, 0x05005713UL, + 0x95BF4A82UL, 0xE2B87A14UL, 0x7BB12BAEUL, 0x0CB61B38UL, 0x92D28E9BUL, 0xE5D5BE0DUL, 0x7CDCEFB7UL, 0x0BDBDF21UL, + 0x86D3D2D4UL, 0xF1D4E242UL, 0x68DDB3F8UL, 0x1FDA836EUL, 0x81BE16CDUL, 0xF6B9265BUL, 0x6FB077E1UL, 0x18B74777UL, + 0x88085AE6UL, 0xFF0F6A70UL, 0x66063BCAUL, 0x11010B5CUL, 0x8F659EFFUL, 0xF862AE69UL, 0x616BFFD3UL, 0x166CCF45UL, + 0xA00AE278UL, 0xD70DD2EEUL, 0x4E048354UL, 0x3903B3C2UL, 0xA7672661UL, 0xD06016F7UL, 0x4969474DUL, 0x3E6E77DBUL, + 0xAED16A4AUL, 0xD9D65ADCUL, 0x40DF0B66UL, 0x37D83BF0UL, 0xA9BCAE53UL, 0xDEBB9EC5UL, 0x47B2CF7FUL, 0x30B5FFE9UL, + 0xBDBDF21CUL, 0xCABAC28AUL, 0x53B39330UL, 0x24B4A3A6UL, 0xBAD03605UL, 0xCDD70693UL, 0x54DE5729UL, 0x23D967BFUL, + 0xB3667A2EUL, 0xC4614AB8UL, 0x5D681B02UL, 0x2A6F2B94UL, 0xB40BBE37UL, 0xC30C8EA1UL, 0x5A05DF1BUL, 0x2D02EF8DUL +}; + +guint32 +metadata_crc32 (const void *ptr, size_t len) +{ + guint32 crc = 0xFFFFFFFF; + const guint8 *bp = (const guint8 *) ptr; + size_t i; + + for (i=0; i<len; i++) + crc = crcTable[(crc ^ bp[i]) & 0xFF] ^ (crc >> 8); + + return crc ^ 0xFFFFFFFF; +} diff -Nru peony-4.10.0.5/debian/upgrade-utils/desktop-grid/crc32.h peony-4.10.0.5/debian/upgrade-utils/desktop-grid/crc32.h --- peony-4.10.0.5/debian/upgrade-utils/desktop-grid/crc32.h 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/upgrade-utils/desktop-grid/crc32.h 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,49 @@ +/* + * Copyright � 2002, 2003 Sun Microsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Sun Microsystems, Inc. nor the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. + * + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES OR + * LIABILITIES SUFFERED BY LICENSEE AS A RESULT OF OR RELATING TO USE, + * MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR ITS DERIVATIVES. + * IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, + * PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, + * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE + * THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE + * SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +/* $Id$ */ +/* @(#)crc32.h 1.6 03/01/08 SMI */ + +/* + * + * @file crc32.h + * @brief CRC-32 calculation function + * @author Alexander Gelfenbain + * + */ + +#include <glib.h> + +guint32 metadata_crc32(const void *ptr, size_t len); diff -Nru peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-conf-parser.1 peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-conf-parser.1 --- peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-conf-parser.1 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-conf-parser.1 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,15 @@ +.\" Man Page for meta-conf-parser +.TH "meta-conf-parser" "1" +.SH "NAME" +meta-conf-parser \- utils for parsing file meta info. +.SH "SYNOPSIS" +.B meta-conf-paser [path] +.SH "DESCRIPTION" +meta-conf-paser +This program is used to parse config generated from update-desktop-grid +.SH "BUGS" +.SS Should you encounter any bugs, they may be reported at: +https://gitee.com/openkylin/peony/issues +.SH "AUTHORS" +.SS This Manual Page has been written for the UKUI Desktop Environment by: +Yue Lan <lanyue@kylinos.cn> (2024) diff -Nru peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-get.1 peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-get.1 --- peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-get.1 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-get.1 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,24 @@ +.\" Man Page for meta-get +.TH "meta-get" "1" +.SH "NAME" +meta-get \- utils for get file meta info. +.SH "SYNOPSIS" +.B meta-get [OPTION] <path> [keys..] +.SH "DESCRIPTION" +meta-get +This program provides several commands for get file meta info. +.TP +\fB -t, --tree\fR +Tree +.TP +\fB -f, --file\fR +Tree file +.TP +\fB -r, --recursive\fR +Recursive +.SH "BUGS" +.SS Should you encounter any bugs, they may be reported at: +https://gitee.com/openkylin/peony/issues +.SH "AUTHORS" +.SS This Manual Page has been written for the UKUI Desktop Environment by: +Yue Lan <lanyue@kylinos.cn> (2024) diff -Nru peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-get.c peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-get.c --- peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-get.c 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-get.c 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,199 @@ +//#include "config.h" +#include "metatree.h" +#include <glib/gstdio.h> + +static char *treename = NULL; +static char *treefilename = NULL; +static gboolean recursive = FALSE; +static GOptionEntry entries[] = +{ + { "tree", 't', 0, G_OPTION_ARG_STRING, &treename, "Tree", NULL}, + { "file", 'f', 0, G_OPTION_ARG_STRING, &treefilename, "Tree file", NULL}, + { "recursive", 'r', 0, G_OPTION_ARG_NONE, &recursive, "Recursive", NULL}, + { NULL } +}; + +static gboolean +print_key (const char *key, + MetaKeyType type, + gpointer value, + gpointer user_data) +{ + int indent = GPOINTER_TO_INT (user_data); + char **values; + int i; + + g_assert (type != META_KEY_TYPE_NONE); + + if (type == META_KEY_TYPE_STRING) + g_print ("%*s%s=%s\n", indent, "",key, (char *)value); + else + { + values = value; + g_print ("%*s%s=[", indent, "",key); + for (i = 0; values[i] != NULL; i++) + { + if (values[i+1] != NULL) + g_print ("%s,", values[i]); + else + g_print ("%s", values[i]); + } + g_print ("]\n"); + } + return TRUE; +} + +static gboolean +prepend_name (const char *entry, + guint64 last_changed, + gboolean has_children, + gboolean has_data, + gpointer user_data) +{ + GList **children = user_data; + + *children = g_list_prepend (*children, + g_strdup (entry)); + return TRUE; +} + +static void +enum_keys (MetaTree *tree, char *path, + gboolean recurse, int indent) +{ + GList *children, *l; + char *child_name, *child_path; + + g_print ("%*s%s\n", indent, "", path); + meta_tree_enumerate_keys (tree, path, + print_key, GINT_TO_POINTER (indent+1)); + + if (recurse) + { + children = NULL; + meta_tree_enumerate_dir (tree, path, + prepend_name, + &children); + for (l = children; l != NULL; l = l->next) + { + child_name = l->data; + child_path = g_build_filename (path, l->data, NULL); + g_free (child_name); + + enum_keys (tree, child_path, recurse, indent + 3); + + g_free (child_path); + } + g_list_free (children); + } +} + +int +main (int argc, + char *argv[]) +{ + MetaTree *tree; + GError *error = NULL; + GOptionContext *context; + MetaKeyType type; + const char *path, *key; + MetaLookupCache *lookup; + struct stat statbuf; + char *tree_path; + char **strings; + int i, j; + + context = g_option_context_new ("<path> [keys..]- read metadata"); + g_option_context_add_main_entries (context, entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("option parsing failed: %s\n", error->message); + return 1; + } + + if (argc < 2) + { + g_printerr ("no path specified\n"); + return 1; + } + path = argv[1]; + + if (treefilename) + { + tree = meta_tree_open (treefilename, FALSE); + if (tree) + tree_path = g_strdup (path); + + if (tree == NULL) + { + g_printerr ("can't open metadata file %s\n", treefilename); + return 1; + } + } + else if (treename) + { + tree = meta_tree_lookup_by_name (treename, FALSE); + if (tree) + tree_path = g_strdup (path); + + if (tree == NULL) + { + g_printerr ("can't open metadata tree %s\n", path); + return 1; + } + } + else + { + lookup = meta_lookup_cache_new (); + if (g_lstat (path, &statbuf) != 0) + { + g_printerr ("can't find file %s\n", path); + return 1; + } + tree = meta_lookup_cache_lookup_path (lookup, + path, + statbuf.st_dev, + FALSE, + &tree_path); + meta_lookup_cache_free (lookup); + + if (tree == NULL) + { + g_printerr ("can't open metadata tree for file %s\n", path); + return 1; + } + } + + if (argc > 2) + { + for (i = 2; i < argc; i++) + { + key = argv[i]; + type = meta_tree_lookup_key_type (tree, tree_path, key); + if (type == META_KEY_TYPE_NONE) + g_print ("%s Not set\n", key); + else if (type == META_KEY_TYPE_STRING) { + g_print ("%s\n", meta_tree_lookup_string (tree, path, key)); + } + else + { + strings = meta_tree_lookup_stringv (tree, path, key); + g_print ("%s=[", key); + for (j = 0; strings[j] != NULL; j++) + { + if (strings[j+1] == NULL) + g_print ("%s", strings[j]); + else + g_print ("%s,", strings[j]); + } + g_print ("]\n"); + } + } + } + else + { + enum_keys (tree, tree_path, recursive, 0); + } + + return 0; +} diff -Nru peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-set.1 peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-set.1 --- peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-set.1 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-set.1 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,24 @@ +.\" Man Page for meta-set +.TH "meta-set" "1" +.SH "NAME" +meta-set \- utils for get file meta info. +.SH "SYNOPSIS" +.B meta-set [OPTION] <path> [keys..] +.SH "DESCRIPTION" +meta-set +This program provides several commands for set file meta info. +.TP +\fB -t, --tree\fR +Tree +.TP +\fB -u, --unset\fR +Unset +.TP +\fB -l, --list\fR +Set as list +.SH "BUGS" +.SS Should you encounter any bugs, they may be reported at: +https://gitee.com/openkylin/peony/issues +.SH "AUTHORS" +.SS This Manual Page has been written for the UKUI Desktop Environment by: +Yue Lan <lanyue@kylinos.cn> (2024) diff -Nru peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-set.c peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-set.c --- peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-set.c 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/upgrade-utils/desktop-grid/meta-set.c 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,124 @@ +#include "metatree.h" +#include <glib/gstdio.h> +#include <gio/gio.h> + +static gboolean unset = FALSE; +static gboolean list = FALSE; +static gboolean use_dbus = FALSE; +static char *treename = NULL; +static GOptionEntry entries[] = +{ + { "tree", 't', 0, G_OPTION_ARG_STRING, &treename, "Tree", NULL}, + { "unset", 'u', 0, G_OPTION_ARG_NONE, &unset, "Unset", NULL }, + { "list", 'l', 0, G_OPTION_ARG_NONE, &list, "Set as list", NULL }, + { NULL } +}; + +int +main (int argc, + char *argv[]) +{ + MetaTree *tree; + GError *error = NULL; + GOptionContext *context; + MetaLookupCache *lookup; + struct stat statbuf; + const char *path, *key; + const char *metatreefile; + char *tree_path; + + context = g_option_context_new ("<path> <key> <value> - set metadata"); + g_option_context_add_main_entries (context, entries, NULL); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("option parsing failed: %s\n", error->message); + return 1; + } + + if (argc < 2) + { + g_printerr ("no path specified\n"); + return 1; + } + path = argv[1]; + + if (argc < 3) + { + g_printerr ("no key specified\n"); + return 1; + } + key = argv[2]; + + if (!list && !unset && argc != 4) + { + g_print ("No value specified\n"); + return 1; + } + + if (treename) + { + tree = meta_tree_lookup_by_name (treename, TRUE); + if (tree) + tree_path = g_strdup (path); + + if (tree == NULL) + { + g_printerr ("can't open metadata tree %s\n", path); + return 1; + } + } + else + { + lookup = meta_lookup_cache_new (); + if (g_lstat (path, &statbuf) != 0) + { + g_printerr ("can't find file %s\n", path); + return 1; + } + tree = meta_lookup_cache_lookup_path (lookup, + path, + statbuf.st_dev, + TRUE, + &tree_path); + meta_lookup_cache_free (lookup); + + if (tree == NULL) + { + g_printerr ("can't open metadata tree for file %s\n", path); + return 1; + } + } + + if (unset) + { + { + if (!meta_tree_unset (tree, tree_path, key)) + { + g_printerr ("Unable to unset key\n"); + return 1; + } + } + } + else if (list) + { + { + if (!meta_tree_set_stringv (tree, tree_path, key, &argv[3])) + { + g_printerr ("Unable to set key\n"); + return 1; + } + } + } + else + { + { + if (!meta_tree_set_string (tree, tree_path, key, argv[3])) + { + g_printerr ("Unable to set key\n"); + return 1; + } + } + } + + return 0; +} diff -Nru peony-4.10.0.5/debian/upgrade-utils/desktop-grid/metabuilder.c peony-4.10.0.5/debian/upgrade-utils/desktop-grid/metabuilder.c --- peony-4.10.0.5/debian/upgrade-utils/desktop-grid/metabuilder.c 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/upgrade-utils/desktop-grid/metabuilder.c 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,1260 @@ +//#include "config.h" +#include "metabuilder.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <glib/gstdio.h> + +#if HAVE_SYS_STATFS_H +#include <sys/statfs.h> +#endif +#if HAVE_SYS_STATVFS_H +#include <sys/statvfs.h> +#endif +#if HAVE_SYS_VFS_H +#include <sys/vfs.h> +#elif HAVE_SYS_MOUNT_H +#if HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif +#include <sys/mount.h> +#endif + +#if defined(HAVE_STATFS) && defined(HAVE_STATVFS) +/* Some systems have both statfs and statvfs, pick the + most "native" for these */ +# if !defined(HAVE_STRUCT_STATFS_F_BAVAIL) + /* on solaris and irix, statfs doesn't even have the + f_bavail field */ +# define USE_STATVFS +# else + /* at least on linux, statfs is the actual syscall */ +# define USE_STATFS +# endif + +#elif defined(HAVE_STATFS) + +# define USE_STATFS + +#elif defined(HAVE_STATVFS) + +# define USE_STATVFS + +#endif + + +#define MAJOR_VERSION 1 +#define MINOR_VERSION 0 +#define MAJOR_JOURNAL_VERSION 1 +#define MINOR_JOURNAL_VERSION 0 +#define NEW_JOURNAL_SIZE (32*1024) + +#define RANDOM_TAG_OFFSET 12 +#define ROTATED_OFFSET 8 + +#define KEY_IS_LIST_MASK (1<<31) + +MetaBuilder * +meta_builder_new (void) +{ + MetaBuilder *builder; + + builder = g_new0 (MetaBuilder, 1); + builder->root = metafile_new ("/", NULL); + + return builder; +} + +void +meta_builder_free (MetaBuilder *builder) +{ + if (builder->root) + metafile_free (builder->root); + g_free (builder); +} + +static gint +compare_metafile (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + const MetaFile *aa, *bb; + + aa = a; + bb = b; + return strcmp (aa->name, bb->name); +} + +static gint +compare_metadata (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + const MetaData *aa, *bb; + + aa = a; + bb = b; + return strcmp (aa->key, bb->key); +} + +static void +metadata_free (MetaData *data) +{ + g_free (data->key); + if (data->is_list) + g_list_free_full (data->values, g_free); + else + g_free (data->value); + + g_free (data); +} + +MetaFile * +metafile_new (const char *name, + MetaFile *parent) +{ + MetaFile *f; + + f = g_new0 (MetaFile, 1); + f->name = g_strdup (name); + f->children = g_sequence_new ((GDestroyNotify)metafile_free); + f->data = g_sequence_new ((GDestroyNotify)metadata_free); + if (parent) + g_sequence_insert_sorted (parent->children, f, compare_metafile, NULL); + + return f; +} + +static MetaData * +metadata_new (const char *key, + MetaFile *file) +{ + MetaData *data; + + data = g_new0 (MetaData, 1); + data->key = g_strdup (key); + + if (file) + g_sequence_insert_sorted (file->data, data, compare_metadata, NULL); + + return data; +} + +static MetaData * +metadata_dup (MetaFile *file, + MetaData *data) +{ + MetaData *new_data; + GList *l; + + new_data = metadata_new (data->key, file); + + new_data->is_list = data->is_list; + if (data->is_list) + { + for (l = data->values; l != NULL; l = l->next) + new_data->values = + g_list_prepend (new_data->values, g_strdup (l->data)); + new_data->values = g_list_reverse (new_data->values); + } + else + new_data->value = g_strdup (data->value); + + return new_data; +} + +void +metafile_free (MetaFile *file) +{ + g_free (file->name); + g_sequence_free (file->children); + g_sequence_free (file->data); + g_free (file); +} + +MetaFile * +metafile_lookup_child (MetaFile *metafile, + const char *name, + gboolean create) +{ + MetaFile *child; + MetaFile lookup_file; + GSequenceIter *lookup_file_iter; + + lookup_file.name = (char *)name; + + lookup_file_iter = g_sequence_lookup (metafile->children, + &lookup_file, + compare_metafile, + NULL); + + if (lookup_file_iter) + return g_sequence_get (lookup_file_iter); + + child = NULL; + if (create) + child = metafile_new (name, metafile); + return child; +} + +static MetaFile * +meta_builder_lookup_with_parent (MetaBuilder *builder, + const char *path, + gboolean create, + MetaFile **parent) +{ + MetaFile *f, *last; + const char *element_start; + char *element; + + last = NULL; + f = builder->root; + while (f) + { + while (*path == '/') + path++; + + if (*path == 0) + break; /* Found it! */ + + element_start = path; + while (*path != 0 && *path != '/') + path++; + element = g_strndup (element_start, path - element_start); + + last = f; + f = metafile_lookup_child (f, element, create); + g_free (element); + } + + if (parent) + *parent = last; + + return f; +} + + +MetaFile * +meta_builder_lookup (MetaBuilder *builder, + const char *path, + gboolean create) +{ + return meta_builder_lookup_with_parent (builder, path, create, NULL); +} + +void +meta_builder_remove (MetaBuilder *builder, + const char *path, + guint64 mtime) +{ + MetaFile *f, *parent; + + f = meta_builder_lookup_with_parent (builder, path, FALSE, &parent); + + if (f == NULL) + return; + + if (parent != NULL) + { + GSequenceIter *iter; + + iter = g_sequence_lookup (parent->children, + f, + compare_metafile, + NULL); + g_sequence_remove (iter); + + if (mtime) + parent->last_changed = mtime; + } + else + { + /* Removing root not allowed, just remove children */ + g_sequence_remove_range (g_sequence_get_begin_iter (f->children), + g_sequence_get_end_iter (f->children)); + if (mtime) + f->last_changed = mtime; + } +} + + +static void +meta_file_copy_into (MetaFile *src, + MetaFile *dest, + guint64 mtime) +{ + MetaFile *src_child, *dest_child; + GSequenceIter *iter; + + if (mtime) + dest->last_changed = mtime; + else + dest->last_changed = src->last_changed; + + for (iter = g_sequence_get_begin_iter (src->data); + iter != g_sequence_get_end_iter (src->data); + iter = g_sequence_iter_next (iter)) + metadata_dup (dest, g_sequence_get (iter)); + + for (iter = g_sequence_get_begin_iter (src->children); + iter != g_sequence_get_end_iter (src->children); + iter = g_sequence_iter_next (iter)) + { + src_child = g_sequence_get (iter); + dest_child = metafile_new (src_child->name, dest); + meta_file_copy_into (src_child, dest_child, mtime); + } +} + +void +meta_builder_copy (MetaBuilder *builder, + const char *source_path, + const char *dest_path, + guint64 mtime) +{ + MetaFile *src, *dest, *temp; + + meta_builder_remove (builder, dest_path, mtime); + + src = meta_builder_lookup (builder, source_path, FALSE); + if (src == NULL) + return; + + temp = metafile_new (NULL, NULL); + meta_file_copy_into (src, temp, mtime); + + dest = meta_builder_lookup (builder, dest_path, TRUE); + g_sequence_free (dest->data); + g_sequence_free (dest->children); + dest->data = temp->data; + dest->children = temp->children; + dest->last_changed = temp->last_changed; + + g_free (temp); +} + +void +metafile_set_mtime (MetaFile *file, + guint64 mtime) +{ + file->last_changed = mtime; +} + +static GSequenceIter * +metafile_key_lookup_iter (MetaFile *file, + const char *key) +{ + MetaData lookup_data; + + lookup_data.key = (char *)key; + + return g_sequence_lookup (file->data, + &lookup_data, + compare_metadata, + NULL); +} + +MetaData * +metafile_key_lookup (MetaFile *file, + const char *key, + gboolean create) +{ + MetaData *data; + GSequenceIter *iter; + + iter = metafile_key_lookup_iter (file, key); + if (iter) + return g_sequence_get (iter); + + data = NULL; + if (create) + data = metadata_new (key, file); + + return data; +} + +static void +metadata_clear (MetaData *data) +{ + if (data->is_list) + { + g_list_free_full (data->values, g_free); + data->values = NULL; + } + else + { + g_free (data->value); + } +} + +void +metafile_key_unset (MetaFile *metafile, + const char *key) +{ + GSequenceIter *iter; + + iter = metafile_key_lookup_iter (metafile, key); + if (iter) + g_sequence_remove (iter); +} + +void +metafile_key_set_value (MetaFile *metafile, + const char *key, + const char *value) +{ + MetaData *data; + + data = metafile_key_lookup (metafile, key, TRUE); + metadata_clear (data); + data->is_list = FALSE; + data->value = g_strdup (value); +} + +void +metafile_key_list_set (MetaFile *metafile, + const char *key) +{ + MetaData *data; + + data = metafile_key_lookup (metafile, key, TRUE); + if (!data->is_list) + { + metadata_clear (data); + data->is_list = TRUE; + } + g_list_free_full (data->values, g_free); + data->values = NULL; +} + +void +metafile_key_list_add (MetaFile *metafile, + const char *key, + const char *value) +{ + MetaData *data; + + data = metafile_key_lookup (metafile, key, TRUE); + if (!data->is_list) + { + metadata_clear (data); + data->is_list = TRUE; + } + + data->values = g_list_append (data->values, g_strdup (value)); +} + +static void +metafile_print (MetaFile *file, int indent, char *parent) +{ + GSequenceIter *iter; + GList *v; + MetaData *data; + char *dir; + + if (parent) + dir = g_strconcat (parent, "/", file->name, NULL); + else + dir = g_strdup (""); + + if (parent) + { + g_print ("%*s%s\n", indent, "", dir); + indent += 3; + } + + for (iter = g_sequence_get_begin_iter (file->data); + iter != g_sequence_get_end_iter (file->data); + iter = g_sequence_iter_next (iter)) + { + data = g_sequence_get (iter); + g_print ("%*s%s=", indent, "", data->key); + if (data->is_list) + { + for (v = data->values; v != NULL; v = v->next) + { + g_print ("%s", (char *)v->data); + if (v->next != NULL) + g_print (", "); + } + } + else + g_print ("%s", data->value); + g_print ("\n"); + } + for (iter = g_sequence_get_begin_iter (file->children); + iter != g_sequence_get_end_iter (file->children); + iter = g_sequence_iter_next (iter)) + { + metafile_print (g_sequence_get (iter), indent, dir); + } + + g_free (dir); +} + +void +meta_builder_print (MetaBuilder *builder) +{ + metafile_print (builder->root, 0, NULL); +} + +static void +set_uint32 (GString *s, guint32 offset, guint32 val) +{ + union { + guint32 as_int; + char as_bytes[4]; + } u; + + u.as_int = GUINT32_TO_BE (val); + memcpy (s->str + offset, u.as_bytes, 4); +} + +static GString * +append_uint32 (GString *s, guint32 val, guint32 *offset) +{ + union { + guint32 as_int; + char as_bytes[4]; + } u; + + if (offset) + *offset = s->len; + + u.as_int = GUINT32_TO_BE (val); + + g_string_append_len (s, u.as_bytes, 4); + + return s; +} + +static GString * +append_time_t (GString *s, gint64 val, MetaBuilder *builder) +{ + guint32 offset; + + if (val == 0) + offset = 0; + else if (val <= builder->time_t_base) + offset = 1; + else + offset = val - builder->time_t_base; + + return append_uint32 (s, offset, NULL); +} + +static GString * +append_int64 (GString *s, gint64 val) +{ + union { + gint64 as_int; + char as_bytes[8]; + } u; + + u.as_int = GINT64_TO_BE (val); + + g_string_append_len (s, u.as_bytes, 8); + + return s; +} + +static void +metafile_collect_times (MetaFile *file, + gint64 *time_t_min, + gint64 *time_t_max) +{ + GSequenceIter *iter; + MetaFile *child; + + if (*time_t_min == 0) + *time_t_min = file->last_changed; + else if (file->last_changed != 0 && file->last_changed < *time_t_min) + *time_t_min = file->last_changed; + + if (file->last_changed > *time_t_max) + *time_t_max = file->last_changed; + + for (iter = g_sequence_get_begin_iter (file->children); + iter != g_sequence_get_end_iter (file->children); + iter = g_sequence_iter_next (iter)) + { + child = g_sequence_get (iter); + metafile_collect_times (child, time_t_min, time_t_max); + } +} + +static void +metafile_collect_keywords (MetaFile *file, + GHashTable *hash) +{ + GSequenceIter *iter; + MetaData *data; + MetaFile *child; + + file->metadata_pointer = 0; + file->children_pointer = 0; + + for (iter = g_sequence_get_begin_iter (file->data); + iter != g_sequence_get_end_iter (file->data); + iter = g_sequence_iter_next (iter)) + { + data = g_sequence_get (iter); + g_hash_table_insert (hash, data->key, GINT_TO_POINTER (1)); + } + + for (iter = g_sequence_get_begin_iter (file->children); + iter != g_sequence_get_end_iter (file->children); + iter = g_sequence_iter_next (iter)) + { + child = g_sequence_get (iter); + metafile_collect_keywords (child, hash); + } +} + +static GHashTable * +string_block_begin (void) +{ + return g_hash_table_new (g_str_hash, g_str_equal); +} + +static void +append_string (GString *out, + const char *string, + GHashTable *string_block) +{ + guint32 offset; + GQueue *offsets; + + append_uint32 (out, 0xdeaddead, &offset); + + if (!g_hash_table_lookup_extended (string_block, + string, NULL, + (gpointer *)&offsets)) + { + offsets = g_queue_new (); + + g_hash_table_insert (string_block, + (char *)string, + offsets); + } + + g_queue_push_tail (offsets, GUINT_TO_POINTER (offset)); +} + +static void +string_block_end (GString *out, + GHashTable *string_block) +{ + char *string; + GQueue *offsets; + GList *l; + guint32 string_offset, offset; + GHashTableIter iter; + + g_hash_table_iter_init (&iter, string_block); + while (g_hash_table_iter_next (&iter, + (gpointer *)&string, + (gpointer *)&offsets)) + { + string_offset = out->len; + g_string_append_len (out, string, strlen (string) + 1); + for (l = g_queue_peek_head_link (offsets); l != NULL; l = l->next) + { + offset = GPOINTER_TO_UINT (l->data); + set_uint32 (out, offset, string_offset); + } + g_queue_free (offsets); + } + + g_hash_table_destroy (string_block); + + /* Pad to 32bit */ + while (out->len % 4 != 0) + g_string_append_c (out, 0); +} + + +static GList * +stringv_block_begin (void) +{ + return NULL; +} + + +typedef struct { + guint32 offset; + GList *strings; +} StringvInfo; + +static void +append_stringv (GString *out, + GList *strings, + GList **stringv_block) +{ + guint32 offset; + StringvInfo *info; + + append_uint32 (out, 0xdeaddead, &offset); + + info = g_new (StringvInfo, 1); + info->offset = offset; + info->strings = strings; + + *stringv_block = g_list_prepend (*stringv_block, info); +} + +static void +stringv_block_end (GString *out, + GHashTable *string_block, + GList *stringv_block) +{ + guint32 table_offset; + StringvInfo *info; + GList *l, *s; + + + for (l = stringv_block; l != NULL; l = l->next) + { + info = l->data; + + table_offset = out->len; + + append_uint32 (out, g_list_length (info->strings), NULL); + for (s = info->strings; s != NULL; s = s->next) + append_string (out, s->data, string_block); + + set_uint32 (out, info->offset, table_offset); + + g_free (info); + } + + g_list_free (stringv_block); + + /* Pad to 32bit */ + while (out->len % 4 != 0) + g_string_append_c (out, 0); +} + +static void +write_children (GString *out, + MetaBuilder *builder) +{ + GHashTable *strings; + MetaFile *child, *file; + GSequenceIter *iter; + GQueue *files; + + files = g_queue_new (); + + g_queue_push_tail (files, builder->root); + + while (!g_queue_is_empty (files)) + { + file = g_queue_pop_head (files); + + if (file->children == NULL) + continue; /* No children, skip file */ + + strings = string_block_begin (); + + if (file->children_pointer != 0) + set_uint32 (out, file->children_pointer, out->len); + + append_uint32 (out, g_sequence_get_length (file->children), NULL); + + for (iter = g_sequence_get_begin_iter (file->children); + iter != g_sequence_get_end_iter (file->children); + iter = g_sequence_iter_next (iter)) + { + child = g_sequence_get (iter); + + /* No mtime, children or metadata, no need for this + to be in the file */ + if (child->last_changed == 0 && + child->children == NULL && + child->data == NULL) + continue; + + append_string (out, child->name, strings); + append_uint32 (out, 0, &child->children_pointer); + append_uint32 (out, 0, &child->metadata_pointer); + append_time_t (out, child->last_changed, builder); + + if (child->children) + g_queue_push_tail (files, child); + } + + string_block_end (out, strings); + } + + g_queue_free (files); +} + +static void +write_metadata_for_file (GString *out, + MetaFile *file, + GList **stringvs, + GHashTable *strings, + GHashTable *key_hash) +{ + GSequenceIter *iter; + MetaData *data; + guint32 key; + + g_assert (file->metadata_pointer != 0); + set_uint32 (out, file->metadata_pointer, out->len); + + append_uint32 (out, g_sequence_get_length (file->data), NULL); + + for (iter = g_sequence_get_begin_iter (file->data); + iter != g_sequence_get_end_iter (file->data); + iter = g_sequence_iter_next (iter)) + { + data = g_sequence_get (iter); + + key = GPOINTER_TO_UINT (g_hash_table_lookup (key_hash, data->key)); + if (data->is_list) + key |= KEY_IS_LIST_MASK; + append_uint32 (out, key, NULL); + if (data->is_list) + append_stringv (out, data->values, stringvs); + else + append_string (out, data->value, strings); + } +} + +static void +write_metadata (GString *out, + MetaBuilder *builder, + GHashTable *key_hash) +{ + GHashTable *strings; + GList *stringvs; + MetaFile *child, *file; + GSequenceIter *iter; + GQueue *files; + + /* Root metadata */ + if (builder->root->data != NULL) + { + strings = string_block_begin (); + stringvs = stringv_block_begin (); + write_metadata_for_file (out, builder->root, + &stringvs, strings, key_hash); + stringv_block_end (out, strings, stringvs); + string_block_end (out, strings); + } + + /* the rest, breadth first with all files in one + dir sharing string block */ + files = g_queue_new (); + + g_queue_push_tail (files, builder->root); + + while (!g_queue_is_empty (files)) + { + file = g_queue_pop_head (files); + + if (file->children == NULL) + continue; /* No children, skip file */ + + strings = string_block_begin (); + stringvs = stringv_block_begin (); + + for (iter = g_sequence_get_begin_iter (file->children); + iter != g_sequence_get_end_iter (file->children); + iter = g_sequence_iter_next (iter)) + { + child = g_sequence_get (iter); + + if (child->data != NULL) + write_metadata_for_file (out, child, + &stringvs, strings, key_hash); + + if (child->children != NULL) + g_queue_push_tail (files, child); + } + + stringv_block_end (out, strings, stringvs); + string_block_end (out, strings); + } + + g_queue_free (files); +} + +static gboolean +write_all_data_and_close (int fd, char *data, gsize len) +{ + gssize written; + gboolean res; + + res = FALSE; + + while (len > 0) + { + written = write (fd, data, len); + + if (written < 0) + { + if (errno == EAGAIN) + continue; + goto out; + } + else if (written == 0) + goto out; /* WTH? Don't loop forever*/ + + len -= written; + data += written; + } + + if (fsync (fd) == -1) + goto out; + + res = TRUE; /* Succeeded! */ + + out: + if (close (fd) == -1) + res = FALSE; + + return res; +} + +gboolean +meta_builder_is_on_nfs (const char *filename) +{ +#ifdef USE_STATFS + struct statfs statfs_buffer; + int statfs_result; +#elif defined(USE_STATVFS) && defined(HAVE_STRUCT_STATVFS_F_BASETYPE) + struct statvfs statfs_buffer; + int statfs_result; +#endif + char *dirname; + gboolean res; + + dirname = g_path_get_dirname (filename); + + res = FALSE; + +#ifdef USE_STATFS + +# if STATFS_ARGS == 2 + statfs_result = statfs (dirname, &statfs_buffer); +# elif STATFS_ARGS == 4 + statfs_result = statfs (dirname, &statfs_buffer, + sizeof (statfs_buffer), 0); +# endif + if (statfs_result == 0) +#ifdef __OpenBSD__ + res = strcmp(statfs_buffer.f_fstypename, MOUNT_NFS) == 0; +#else + res = statfs_buffer.f_type == 0x6969; +#endif + +#elif defined(USE_STATVFS) && defined(HAVE_STRUCT_STATVFS_F_BASETYPE) + statfs_result = statvfs (dirname, &statfs_buffer); + + if (statfs_result == 0) + res = strcmp (statfs_buffer.f_basetype, "nfs") == 0; +#endif + + g_free (dirname); + + return res; +} + +static char * +get_runtime_journal_dir (const char *tree_filename) +{ + const char *rd; + char *dbname; + char *real_path; + char *ret; + + rd = g_get_user_runtime_dir (); + if (! rd || *rd == '\0') + return NULL; + + real_path = g_build_filename (rd, "gvfs-metadata", NULL); + if (! g_file_test (real_path, G_FILE_TEST_EXISTS)) + { + if (g_mkdir_with_parents (real_path, 0700) != 0) + { + g_free (real_path); + return NULL; + } + } + + dbname = g_path_get_basename (tree_filename); + ret = g_build_filename (real_path, dbname, NULL); + + g_free (dbname); + g_free (real_path); + + return ret; +} + +char * +meta_builder_get_journal_filename (const char *tree_filename, guint32 random_tag) +{ + const char *hexdigits = "0123456789abcdef"; + char tag[9]; + int i; + char *ret; + char *real_filename = NULL; + + for (i = 7; i >= 0; i--) + { + tag[i] = hexdigits[random_tag % 0x10]; + random_tag >>= 4; + } + + tag[8] = 0; + + if (meta_builder_is_on_nfs (tree_filename)) + { + /* Put the journal in $XDG_RUNTIME_DIR to avoid file usage from concurrent clients */ + real_filename = get_runtime_journal_dir (tree_filename); + } + + if (! real_filename) + return g_strconcat (tree_filename, "-", tag, ".log", NULL); + + ret = g_strconcat (real_filename, "-", tag, ".log", NULL); + g_free (real_filename); + return ret; +} + +gboolean +meta_builder_create_new_journal (const char *filename, guint32 random_tag) +{ + char *journal_name; + guint32 size_offset; + GString *out; + gsize pos; + gboolean res; + + journal_name = meta_builder_get_journal_filename (filename, random_tag); + + out = g_string_new (NULL); + + /* HEADER */ + g_string_append_c (out, 0xda); + g_string_append_c (out, 0x1a); + g_string_append_c (out, 'j'); + g_string_append_c (out, 'o'); + g_string_append_c (out, 'u'); + g_string_append_c (out, 'r'); + + /* VERSION */ + g_string_append_c (out, MAJOR_JOURNAL_VERSION); + g_string_append_c (out, MINOR_JOURNAL_VERSION); + + append_uint32 (out, random_tag, NULL); + append_uint32 (out, 0, &size_offset); + append_uint32 (out, 0, NULL); /* Num entries, none so far */ + + pos = out->len; + + g_string_set_size (out, NEW_JOURNAL_SIZE); + memset (out->str + pos, 0, out->len - pos); + + set_uint32 (out, size_offset, out->len); + + res = g_file_set_contents (journal_name, + out->str, out->len, + NULL); + + g_free (journal_name); + g_string_free (out, TRUE); + + return res; +} + +static GString * +metadata_create_static (MetaBuilder *builder, + guint32 *random_tag_out) +{ + GString *out; + GHashTable *hash, *key_hash; + GHashTableIter iter; + char *key; + GList *keys, *l; + GHashTable *strings; + guint32 index; + guint32 attributes_pointer; + gint64 time_t_min; + gint64 time_t_max; + guint32 random_tag, root_name; + + out = g_string_new (NULL); + + /* HEADER */ + g_string_append_c (out, 0xda); + g_string_append_c (out, 0x1a); + g_string_append_c (out, 'm'); + g_string_append_c (out, 'e'); + g_string_append_c (out, 't'); + g_string_append_c (out, 'a'); + + /* VERSION */ + g_string_append_c (out, MAJOR_VERSION); + g_string_append_c (out, MINOR_VERSION); + + append_uint32 (out, 0, NULL); /* Rotated */ + random_tag = g_random_int (); + *random_tag_out = random_tag; + append_uint32 (out, random_tag, NULL); + append_uint32 (out, 0, &builder->root_pointer); + append_uint32 (out, 0, &attributes_pointer); + + time_t_min = 0; + time_t_max = 0; + metafile_collect_times (builder->root, &time_t_min, &time_t_max); + + /* Store the base as the min value in use minus one so that + 0 is free to mean "not defined" */ + if (time_t_min != 0) + time_t_min = time_t_min - 1; + + /* Pick the base as the minimum, unless that leads to + a 32bit overflow */ + if (time_t_max - time_t_min > G_MAXUINT32) + time_t_min = time_t_max - G_MAXUINT32; + builder->time_t_base = time_t_min; + append_int64 (out, builder->time_t_base); + + /* Collect and sort all used keys */ + hash = g_hash_table_new (g_str_hash, g_str_equal); + metafile_collect_keywords (builder->root, hash); + g_hash_table_iter_init (&iter, hash); + keys = NULL; + while (g_hash_table_iter_next (&iter, (gpointer *)&key, NULL)) + keys = g_list_prepend (keys, key); + g_hash_table_destroy (hash); + keys = g_list_sort (keys, (GCompareFunc)strcmp); + + /* Write keys to file and collect mapping for keys */ + set_uint32 (out, attributes_pointer, out->len); + key_hash = g_hash_table_new (g_str_hash, g_str_equal); + strings = string_block_begin (); + append_uint32 (out, g_list_length (keys), NULL); + for (l = keys, index = 0; l != NULL; l = l->next, index++) + { + key = l->data; + append_string (out, key, strings); + g_hash_table_insert (key_hash, key, GUINT_TO_POINTER (index)); + } + string_block_end (out, strings); + + /* update root pointer */ + set_uint32 (out, builder->root_pointer, out->len); + + /* Root name */ + append_uint32 (out, 0, &root_name); + + /* Root child pointer */ + append_uint32 (out, 0, &builder->root->children_pointer); + + /* Root metadata pointer */ + append_uint32 (out, 0, &builder->root->metadata_pointer); + + /* Root last changed */ + append_uint32 (out, builder->root->last_changed, NULL); + + /* Root name */ + set_uint32 (out, root_name, out->len); + g_string_append_len (out, "/", 2); + + /* Pad to 32bit */ + while (out->len % 4 != 0) + g_string_append_c (out, 0); + + write_children (out, builder); + write_metadata (out, builder, key_hash); + + g_hash_table_destroy (key_hash); + g_list_free (keys); + + return out; +} + +gboolean +meta_builder_write (MetaBuilder *builder, + const char *filename) +{ + GString *out; + guint32 random_tag; + int fd, fd2, fd_dir; + char *tmp_name, *dirname; + + out = metadata_create_static (builder, &random_tag); + + tmp_name = g_strdup_printf ("%s.XXXXXX", filename); + fd = g_mkstemp (tmp_name); + if (fd == -1) + goto out; + + if (!write_all_data_and_close (fd, out->str, out->len)) + goto out; + + if (!meta_builder_create_new_journal (filename, random_tag)) + goto out; + + /* Open old file so we can set it rotated */ + fd2 = open (filename, O_RDWR); + if (g_rename (tmp_name, filename) == -1) + { + if (fd2 != -1) + close (fd2); + goto out; + } + + /* Sync the directory to make sure that the entry in the directory containing + the new medata file has also reached disk. */ + dirname = g_path_get_dirname (filename); + fd_dir = open (dirname, O_RDONLY); + if (fd_dir > -1) + { + fsync (fd_dir); + close (fd_dir); + } + g_free (dirname); + + /* Mark old file (if any) as rotated) */ + if (fd2 != -1) + { + guint32 old_tag; + char *old_log; + char *data; + + data = mmap (NULL, RANDOM_TAG_OFFSET + 4, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0); + + if (data) + { + old_tag = GUINT32_FROM_BE (*(guint32 *)(data + RANDOM_TAG_OFFSET)); + *(guint32 *)(data + ROTATED_OFFSET) = 0xffffffff; + munmap (data, RANDOM_TAG_OFFSET + 4); + close (fd2); + + old_log = meta_builder_get_journal_filename (filename, old_tag); + g_unlink (old_log); + g_free (old_log); + } + } + + g_string_free (out, TRUE); + g_free (tmp_name); + return TRUE; + + out: + if (fd != -1) + g_unlink (tmp_name); + g_string_free (out, TRUE); + g_free (tmp_name); + return FALSE; +} diff -Nru peony-4.10.0.5/debian/upgrade-utils/desktop-grid/metabuilder.h peony-4.10.0.5/debian/upgrade-utils/desktop-grid/metabuilder.h --- peony-4.10.0.5/debian/upgrade-utils/desktop-grid/metabuilder.h 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/upgrade-utils/desktop-grid/metabuilder.h 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,98 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2009 Red Hat, Inc. + * + * 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 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. + * + * Author: Alexander Larsson <alexl@redhat.com> + */ + +#ifndef __META_BUILDER_H__ +#define __META_BUILDER_H__ + +#include <glib.h> + +typedef struct _MetaBuilder MetaBuilder; +typedef struct _MetaFile MetaFile; +typedef struct _MetaData MetaData; + +struct _MetaBuilder { + MetaFile *root; + + guint32 root_pointer; + gint64 time_t_base; +}; + +struct _MetaFile { + char *name; + GSequence *children; + gint64 last_changed; + GSequence *data; + + guint32 metadata_pointer; + guint32 children_pointer; +}; + +struct _MetaData { + char *key; + gboolean is_list; + char *value; + GList *values; +}; + +MetaBuilder *meta_builder_new (void); +void meta_builder_free (MetaBuilder *builder); +void meta_builder_print (MetaBuilder *builder); +MetaFile * meta_builder_lookup (MetaBuilder *builder, + const char *path, + gboolean create); +void meta_builder_remove (MetaBuilder *builder, + const char *path, + guint64 mtime); +void meta_builder_copy (MetaBuilder *builder, + const char *source_path, + const char *dest_path, + guint64 mtime); +gboolean meta_builder_write (MetaBuilder *builder, + const char *filename); +gboolean meta_builder_create_new_journal (const char *filename, + guint32 random_tag); +char * meta_builder_get_journal_filename (const char *tree_filename, + guint32 random_tag); +gboolean meta_builder_is_on_nfs (const char *filename); +MetaFile * metafile_new (const char *name, + MetaFile *parent); +void metafile_free (MetaFile *file); +void metafile_set_mtime (MetaFile *file, + guint64 mtime); +MetaFile * metafile_lookup_child (MetaFile *metafile, + const char *name, + gboolean create); +MetaData * metafile_key_lookup (MetaFile *file, + const char *key, + gboolean create); +void metafile_key_unset (MetaFile *metafile, + const char *key); +void metafile_key_set_value (MetaFile *metafile, + const char *key, + const char *value); +void metafile_key_list_set (MetaFile *metafile, + const char *key); +void metafile_key_list_add (MetaFile *metafile, + const char *key, + const char *value); + +#endif /* __META_BUILDER_H__ */ diff -Nru peony-4.10.0.5/debian/upgrade-utils/desktop-grid/metatree.c peony-4.10.0.5/debian/upgrade-utils/desktop-grid/metatree.c --- peony-4.10.0.5/debian/upgrade-utils/desktop-grid/metatree.c 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/upgrade-utils/desktop-grid/metatree.c 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,3336 @@ +//#include "config.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <time.h> + +#include "metatree.h" +#include "metabuilder.h" +#include <glib.h> +#include <glib/gstdio.h> +//#include "gvfsutils.h" +#include "crc32.h" +//#include "metadata-dbus.h" +//#include "gvfsdaemonprotocol.h" + +#if MAJOR_IN_MKDEV +#include <sys/mkdev.h> +#elif MAJOR_IN_SYSMACROS +#include <sys/sysmacros.h> +#endif + +#define MAGIC "\xda\x1ameta" +#define MAGIC_LEN 6 +#define MAJOR_VERSION 1 +#define MINOR_VERSION 0 +#define JOURNAL_MAGIC "\xda\x1ajour" +#define JOURNAL_MAGIC_LEN 6 +#define JOURNAL_MAJOR_VERSION 1 +#define JOURNAL_MINOR_VERSION 0 + +#define KEY_IS_LIST_MASK (1<<31) + +static GRWLock metatree_lock; + +typedef enum { + JOURNAL_OP_SET_KEY, + JOURNAL_OP_SETV_KEY, + JOURNAL_OP_UNSET_KEY, + JOURNAL_OP_COPY_PATH, + JOURNAL_OP_REMOVE_PATH +} MetaJournalEntryType; + +typedef struct { + guchar magic[6]; + guchar major; + guchar minor; + guint32 rotated; + guint32 random_tag; + guint32 root; + guint32 attributes; + guint64 time_t_base; +} MetaFileHeader; + +typedef struct { + guint32 name; + guint32 children; + guint32 metadata; + guint32 last_changed; +} MetaFileDirEnt; + +typedef struct { + guint32 num_children; + MetaFileDirEnt children[1]; +} MetaFileDir; + +typedef struct { + guint32 num_strings; + guint32 strings[1]; +} MetaFileStringv; + +typedef struct { + guint32 key; + guint32 value; +} MetaFileDataEnt; + +typedef struct { + guint32 num_keys; + MetaFileDataEnt keys[1]; +} MetaFileData; + +typedef struct { + guchar magic[6]; + guchar major; + guchar minor; + guint32 random_tag; + guint32 file_size; + guint32 num_entries; +} MetaJournalHeader; + +typedef struct { + guint32 entry_size; + guint32 crc32; + guint64 mtime; + guint8 entry_type; + char path[1]; +} MetaJournalEntry; + +typedef struct { + char *filename; + int fd; + char *data; + gsize len; + + MetaJournalHeader *header; + MetaJournalEntry *first_entry; + guint last_entry_num; + MetaJournalEntry *last_entry; + + gboolean journal_valid; /* True if all entries validated on open */ +} MetaJournal; + +struct _MetaTree { + volatile guint ref_count; + char *filename; + gboolean for_write; + gboolean on_nfs; + + int fd; + char *data; + gsize len; + ino_t inode; + + guint32 tag; + gint64 time_t_base; + MetaFileHeader *header; + MetaFileDirEnt *root; + + int num_attributes; + char **attributes; + + MetaJournal *journal; +}; + +/* Unfortunately the journal entries are only aligned to 32 bit boundaries + * but on some 64-bit RISC architectures (e.g. Alpha) this is insufficient + * to guarantee correct alignment of 64-bit accesses. This is not a show + * stopper but does cause inefficient traps to the kernel and pollution of + * kernel logs. Rather than fix the alignment we provide a helper function, + * dependent on features specific to gcc, to correctly access a 64-bit datum + * that may be misaligned. + * + * See https://bugzilla.gnome.org/show_bug.cgi?id=726456 + */ +#if defined(__GNUC__) && (defined(__alpha__) || defined(__mips__) || defined(__sparc__)) +struct una_u64 { guint64 x __attribute__((packed)); }; +static inline guint64 ldq_u(guint64 *p) +{ + const struct una_u64 *ptr = (const struct una_u64 *) p; + return ptr->x; +} +#else +#define ldq_u(x) (*(x)) +#endif + +static gboolean meta_tree_refresh_locked (MetaTree *tree, + gboolean force_reread); +static MetaJournal *meta_journal_open (MetaTree *tree, + const char *filename, + gboolean for_write, + guint32 tag); +static void meta_journal_free (MetaJournal *journal); +static void meta_journal_validate_more (MetaJournal *journal); + +static gpointer +verify_block_pointer (MetaTree *tree, guint32 pos, guint32 len) +{ + pos = GUINT32_FROM_BE (pos); + + /* Ensure 32bit aligned */ + if (pos %4 != 0) + return NULL; + + if (pos > tree->len) + return NULL; + + if (pos + len < pos || + pos + len > tree->len) + return NULL; + + return tree->data + pos; +} + +static gpointer +verify_array_block (MetaTree *tree, guint32 pos, gsize element_size) +{ + guint32 *nump, num; + + nump = verify_block_pointer (tree, pos, sizeof (guint32)); + if (nump == NULL) + return NULL; + + num = GUINT32_FROM_BE (*nump); + + return verify_block_pointer (tree, pos, sizeof (guint32) + num * element_size); +} + +static gpointer +verify_children_block (MetaTree *tree, guint32 pos) +{ + return verify_array_block (tree, pos, sizeof (MetaFileDirEnt)); +} + +static gpointer +verify_metadata_block (MetaTree *tree, guint32 pos) +{ + return verify_array_block (tree, pos, sizeof (MetaFileDataEnt)); +} + +static char * +verify_string (MetaTree *tree, guint32 pos) +{ + char *str, *ptr, *end; + + pos = GUINT32_FROM_BE (pos); + + if (pos > tree->len) + return NULL; + + str = ptr = tree->data + pos; + end = tree->data + tree->len; + + while (ptr < end && *ptr != 0) + ptr++; + + if (ptr == end) + return NULL; + + return str; +} + +static void +meta_tree_clear (MetaTree *tree) +{ + if (tree->journal) + { + meta_journal_free (tree->journal); + tree->journal = NULL; + } + + g_free (tree->attributes); + tree->num_attributes = 0; + tree->attributes = NULL; + + tree->tag = 0; + tree->time_t_base = 0; + tree->header = NULL; + tree->root = NULL; + + if (tree->data) + { + munmap(tree->data, tree->len); + tree->data = NULL; + } + + tree->len = 0; + if (tree->fd != -1) + { + close (tree->fd); + tree->fd = -1; + } +} + +static gboolean +link_to_tmp (const char *source, char *tmpl) +{ + return FALSE; +} + +static int +safe_open (MetaTree *tree, + char *filename, + int flags) +{ + if (tree->on_nfs) + { + char *dirname, *tmpname; + int fd, errsv; + + /* On NFS if another client unlinks an open file + * it is actually removed on the server and this + * client will get an ESTALE error on later access. + * + * For a local (i.e. on this client) unlink this is + * handled by the kernel keeping track of unlinks of + * open files (by this client) using ".nfsXXXX" files. + * + * We work around the ESTALE problem by first linking + * the file to a temp file that we then unlink on + * this client. We never leak the tmpfile (unless + * the kernel crashes) and no other client should + * remove our tmpfile. + */ + + dirname = g_path_get_dirname (filename); + tmpname = g_build_filename (dirname, ".openXXXXXX", NULL); + g_free (dirname); + + if (!link_to_tmp (filename, tmpname)) + fd = open (filename, flags); /* link failed, what can we do... */ + else + { + fd = open (tmpname, flags); + errsv = errno; + unlink (tmpname); + errno = errsv; + } + + g_free (tmpname); + return fd; + } + else + return open (filename, flags); + +} + +static gboolean +meta_tree_init (MetaTree *tree) +{ + struct stat statbuf; + int fd; + void *data; + guint32 *attributes; + gboolean retried; + int i; + int errsv; + + retried = FALSE; + retry: + tree->on_nfs = meta_builder_is_on_nfs (tree->filename); + fd = safe_open (tree, tree->filename, O_RDONLY); + if (fd == -1) + { + errsv = errno; + + if (tree->for_write && !retried) + { + MetaBuilder *builder; + char *dir; + + dir = g_path_get_dirname (tree->filename); + g_mkdir_with_parents (dir, 0700); + g_free (dir); + + builder = meta_builder_new (); + retried = TRUE; + if (meta_builder_write (builder, tree->filename)) + { + meta_builder_free (builder); + goto retry; + } + meta_builder_free (builder); + } + else if (tree->for_write || errsv != ENOENT) + { + g_warning ("can't init metadata tree %s: open: %s", tree->filename, g_strerror (errsv)); + } + tree->fd = -1; + + /* If we're opening for reading and the file does not exist, it is not + * an error. The file will be created later. */ + return !tree->for_write && errsv == ENOENT; + } + + if (fstat (fd, &statbuf) != 0) + { + errsv = errno; + g_warning ("can't init metadata tree %s: fstat: %s", tree->filename, g_strerror (errsv)); + + close (fd); + return FALSE; + } + + if (statbuf.st_size < sizeof (MetaFileHeader)) + { + g_warning ("can't init metadata tree %s: wrong size", tree->filename); + + close (fd); + return FALSE; + } + + data = mmap (NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) + { + errsv = errno; + g_warning ("can't init metadata tree %s: mmap: %s", tree->filename, g_strerror (errsv)); + + close (fd); + return FALSE; + } + + tree->fd = fd; + tree->len = statbuf.st_size; + tree->inode = statbuf.st_ino; + tree->data = data; + tree->header = (MetaFileHeader *)data; + + if (memcmp (tree->header->magic, MAGIC, MAGIC_LEN) != 0) + { + g_warning ("can't init metadata tree %s: wrong magic", tree->filename); + if (!tree->for_write) + goto err; + + meta_tree_clear (tree); + + if (g_unlink (tree->filename) != 0) + goto err; + + goto retry; + } + + if (tree->header->major != MAJOR_VERSION) + { + g_warning ("can't init metadata tree %s: wrong version", tree->filename); + goto err; + } + + tree->root = verify_block_pointer (tree, tree->header->root, sizeof (MetaFileDirEnt)); + if (tree->root == NULL) + { + g_warning ("can't init metadata tree %s: wrong pointer", tree->filename); + goto err; + } + + attributes = verify_array_block (tree, tree->header->attributes, sizeof (guint32)); + if (attributes == NULL) + { + g_warning ("can't init metadata tree %s: wrong block", tree->filename); + goto err; + } + + tree->num_attributes = GUINT32_FROM_BE (*attributes); + attributes++; + tree->attributes = g_new (char *, tree->num_attributes); + for (i = 0; i < tree->num_attributes; i++) + { + tree->attributes[i] = verify_string (tree, attributes[i]); + if (tree->attributes[i] == NULL) + { + g_warning ("can't init metadata tree %s: wrong attribute", tree->filename); + goto err; + } + } + + tree->tag = GUINT32_FROM_BE (tree->header->random_tag); + tree->time_t_base = GINT64_FROM_BE (tree->header->time_t_base); + + tree->journal = meta_journal_open (tree, tree->filename, tree->for_write, tree->tag); + + /* There is a race with tree replacing, where the journal could have been + deleted (and the tree replaced) inbetween opening the tree file and the + journal. However we can detect this case by looking at the tree and see + if its been rotated, we do this to ensure we have an uptodate tree+journal + combo. */ + return meta_tree_refresh_locked (tree, FALSE); + + err: + meta_tree_clear (tree); + return FALSE; +} + +MetaTree * +meta_tree_open (const char *filename, + gboolean for_write) +{ + MetaTree *tree; + gboolean res; + + g_assert (sizeof (MetaFileHeader) == 32); + g_assert (sizeof (MetaFileDirEnt) == 16); + g_assert (sizeof (MetaFileDataEnt) == 8); + + tree = g_new0 (MetaTree, 1); + tree->ref_count = 1; + tree->filename = g_strdup (filename); + tree->for_write = for_write; + tree->fd = -1; + + res = meta_tree_init (tree); + if (!res) + { + /* do not return uninitialized tree to avoid corruptions */ + meta_tree_unref (tree); + tree = NULL; + } + + return tree; +} + +const char * +meta_tree_get_filename (MetaTree *tree) +{ + return tree->filename; +} + +gboolean +meta_tree_exists (MetaTree *tree) +{ + return tree->fd != -1; +} + +gboolean +meta_tree_is_on_nfs (MetaTree *tree) +{ + return tree->on_nfs; +} + +static GHashTable *cached_trees = NULL; +G_LOCK_DEFINE_STATIC (cached_trees); + +MetaTree * +meta_tree_lookup_by_name (const char *name, + gboolean for_write) +{ + MetaTree *tree; + char *filename; + + G_LOCK (cached_trees); + + if (cached_trees == NULL) + cached_trees = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify)g_free, + (GDestroyNotify)meta_tree_unref); + + tree = g_hash_table_lookup (cached_trees, name); + if (tree && tree->for_write == for_write) + { + meta_tree_ref (tree); + G_UNLOCK (cached_trees); + + if (meta_tree_refresh (tree)) + return tree; + + meta_tree_unref (tree); + return NULL; + } + + filename = g_build_filename (g_get_user_data_dir (), "gvfs-metadata", name, NULL); + tree = meta_tree_open (filename, for_write); + g_free (filename); + + if (tree) + g_hash_table_insert (cached_trees, g_strdup (name), meta_tree_ref (tree)); + + G_UNLOCK (cached_trees); + + return tree; +} + +MetaTree * +meta_tree_ref (MetaTree *tree) +{ + g_atomic_int_inc ((int *)&tree->ref_count); + return tree; +} + +void +meta_tree_unref (MetaTree *tree) +{ + gboolean is_zero; + + is_zero = g_atomic_int_dec_and_test ((int *)&tree->ref_count); + if (is_zero) + { + meta_tree_clear (tree); + g_free (tree->filename); + g_free (tree); + } +} + +static gboolean +meta_tree_needs_rereading (MetaTree *tree) +{ + struct stat statbuf; + + if (tree->fd == -1) + return TRUE; + + if (tree->header != NULL && + GUINT32_FROM_BE (tree->header->rotated) == 0) + return FALSE; /* Got a valid tree and its not rotated */ + + /* Sanity check to avoid infinite loops when a stable file + has the rotated bit set to 1 (see gnome bugzilla bug #600057) */ + + if (lstat (tree->filename, &statbuf) != 0) + return FALSE; + + if (tree->inode == statbuf.st_ino) + return FALSE; + + return TRUE; +} + +static gboolean +meta_tree_has_new_journal_entries (MetaTree *tree) +{ + guint32 num_entries; + MetaJournal *journal; + + journal = tree->journal; + + if (journal == NULL || + !tree->journal->journal_valid) + return FALSE; /* Once we've seen a failure, never look for more */ + + /* TODO: Use atomic read here? */ + num_entries = GUINT32_FROM_BE (*(volatile guint32 *)&journal->header->num_entries); + + return journal->last_entry_num < num_entries; +} + + +/* Must be called with a write lock held */ +static gboolean +meta_tree_refresh_locked (MetaTree *tree, gboolean force_reread) +{ + /* Needs to recheck since we dropped read lock */ + if (force_reread || meta_tree_needs_rereading (tree)) + { + if (tree->header) + meta_tree_clear (tree); + return meta_tree_init (tree); + } + else if (meta_tree_has_new_journal_entries (tree)) + meta_journal_validate_more (tree->journal); + + return TRUE; +} + +/* NB: The tree is uninitialized if FALSE is returned! */ +gboolean +meta_tree_refresh (MetaTree *tree) +{ + gboolean needs_refresh; + gboolean res = TRUE; + + g_rw_lock_reader_lock (&metatree_lock); + needs_refresh = + meta_tree_needs_rereading (tree) || + meta_tree_has_new_journal_entries (tree); + g_rw_lock_reader_unlock (&metatree_lock); + + if (needs_refresh) + { + g_rw_lock_writer_lock (&metatree_lock); + res = meta_tree_refresh_locked (tree, FALSE); + g_rw_lock_writer_unlock (&metatree_lock); + } + + return res; +} + +struct FindName { + MetaTree *tree; + const char *name; +}; + +static int +find_dir_element (const void *_key, const void *_dirent) +{ + const struct FindName *key = _key; + const MetaFileDirEnt *dirent = _dirent; + char *dirent_name; + + dirent_name = verify_string (key->tree, dirent->name); + if (dirent_name == NULL) + return -1; + return strcmp (key->name, dirent_name); +} + +/* modifies path!!! */ +static MetaFileDirEnt * +dir_lookup_path (MetaTree *tree, + MetaFileDirEnt *dirent, + char *path) +{ + char *end_path; + MetaFileDir *dir; + struct FindName key; + + while (*path == '/') + path++; + + if (*path == 0) + return dirent; + + if (dirent->children == 0) + return NULL; + + dir = verify_children_block (tree, dirent->children); + if (dir == NULL) + return NULL; + + end_path = path; + while (*end_path != 0 && + *end_path != '/') + end_path++; + + if (*end_path != 0) + *end_path++ = 0; + + key.name = path; + key.tree = tree; + dirent = bsearch (&key, &dir->children[0], + GUINT32_FROM_BE (dir->num_children), sizeof (MetaFileDirEnt), + find_dir_element); + + if (dirent == NULL) + return NULL; + + return dir_lookup_path (tree, dirent, end_path); +} + +static MetaFileDirEnt * +meta_tree_lookup (MetaTree *tree, + const char *path) +{ + MetaFileDirEnt *dirent; + char *path_copy; + + if (tree->root == NULL) + return NULL; + + path_copy = g_strdup (path); + dirent = dir_lookup_path (tree, tree->root, path_copy); + g_free (path_copy); + + return dirent; +} + +static MetaFileData * +meta_tree_lookup_data (MetaTree *tree, + const char *path) +{ + MetaFileDirEnt *dirent; + MetaFileData *data; + + data = NULL; + dirent = meta_tree_lookup (tree, path); + if (dirent) + data = verify_metadata_block (tree, dirent->metadata); + + return data; +} + +static int +find_attribute_id (const void *_key, const void *_entry) +{ + const char *key = _key; + const char *const*entry = _entry; + + return strcmp (key, *entry); +} + +#define NO_KEY ((guint32)-1) + +static guint32 +get_id_for_key (MetaTree *tree, + const char *attribute) +{ + char **attribute_ptr; + + attribute_ptr = bsearch (attribute, tree->attributes, + tree->num_attributes, sizeof (char *), + find_attribute_id); + + if (attribute_ptr == NULL) + return NO_KEY; + + return attribute_ptr - tree->attributes; +} + +struct FindId { + MetaTree *tree; + guint32 id; +}; + +static int +find_data_element (const void *_key, const void *_dataent) +{ + const struct FindId *key = _key; + const MetaFileDataEnt *dataent = _dataent; + guint32 key_id; + + key_id = GUINT32_FROM_BE (dataent->key) & ~KEY_IS_LIST_MASK; + + return key->id - key_id; +} + +static MetaFileDataEnt * +meta_data_get_key (MetaTree *tree, + MetaFileData *data, + const char *attribute) +{ + MetaFileDataEnt *dataent; + struct FindId key; + + key.id = get_id_for_key (tree, attribute); + key.tree = tree; + dataent = bsearch (&key, &data->keys[0], + GUINT32_FROM_BE (data->num_keys), sizeof (MetaFileDataEnt), + find_data_element); + + return dataent; +} + +static void +meta_journal_free (MetaJournal *journal) +{ + g_free (journal->filename); + munmap(journal->data, journal->len); + close (journal->fd); + g_free (journal); +} + +static MetaJournalEntry * +verify_journal_entry (MetaJournal *journal, + MetaJournalEntry *entry) +{ + guint32 offset, real_crc32; + guint32 entry_len, entry_len_end; + char *ptr; + + ptr = (char *)entry; + if (ptr < journal->data) + return NULL; + offset = ptr - journal->data; + + /* Must be 32bit aligned */ + if (offset % 4 != 0) + return NULL; + + /* entry_size must be valid */ + if (offset > journal->len - 4) + return NULL; + + /* Verify that entry fits and has right size */ + entry_len = GUINT32_FROM_BE (entry->entry_size); + + /* Must be 32bit aligned */ + if (entry_len % 4 != 0) + return NULL; + /* Must have space for at the very least: + len+crc32+mtime+type+path_terminating_zeor+end_len */ + if (journal->len < 4 + 4 + 8 + 1 + 1 + 4) + return NULL; + + if (entry_len > journal->len || + offset > journal->len - entry_len) + return NULL; + + entry_len_end = GUINT32_FROM_BE (*(guint32 *)(journal->data + offset + entry_len - 4)); + if (entry_len != entry_len_end) + return NULL; + + real_crc32 = metadata_crc32 (journal->data + offset + 8, entry_len - 8); + if (real_crc32 != GUINT32_FROM_BE (entry->crc32)) + return NULL; + + return (MetaJournalEntry *)(journal->data + offset + entry_len); +} + +/* Try to validate more entries, call with writer lock */ +static void +meta_journal_validate_more (MetaJournal *journal) +{ + guint32 num_entries, i; + MetaJournalEntry *entry, *next_entry; + + if (!journal->journal_valid) + return; /* Once we've seen a failure, never look for more */ + + /* TODO: Use atomic read here? */ + num_entries = GUINT32_FROM_BE (*(volatile guint32 *)&journal->header->num_entries); + + entry = journal->last_entry; + i = journal->last_entry_num; + while (i < num_entries) + { + next_entry = verify_journal_entry (journal, entry); + + if (next_entry == NULL) + { + journal->journal_valid = FALSE; + break; + } + + entry = next_entry; + i++; + } + + journal->last_entry = entry; + journal->last_entry_num = i; +} + +static void +set_uint32 (GString *s, guint32 offset, guint32 val) +{ + union { + guint32 as_int; + char as_bytes[4]; + } u; + + u.as_int = GUINT32_TO_BE (val); + memcpy (s->str + offset, u.as_bytes, 4); +} + +static GString * +append_uint32 (GString *s, guint32 val) +{ + union { + guint32 as_int; + char as_bytes[4]; + } u; + + u.as_int = GUINT32_TO_BE (val); + g_string_append_len (s, u.as_bytes, 4); + return s; +} + +static GString * +append_uint64 (GString *s, guint64 val) +{ + union { + guint64 as_int; + char as_bytes[8]; + } u; + + u.as_int = GUINT64_TO_BE (val); + g_string_append_len (s, u.as_bytes, 8); + return s; +} + +static GString * +append_string (GString *s, const char *str) +{ + g_string_append (s, str); + g_string_append_c (s, 0); + return s; +} + +static guint64 +get_time_t (MetaTree *tree, guint32 val) +{ + val = GUINT32_FROM_BE (val); + if (val == 0) + return 0; + return val + tree->time_t_base; +} + +static GString * +meta_journal_entry_init (int op, + guint64 mtime, + const char *path) +{ + GString *out; + + out = g_string_new (NULL); + append_uint32 (out, 0); /* len */ + append_uint32 (out, 0); /* crc32 */ + append_uint64 (out, mtime); + g_string_append_c (out, (char)op); + append_string (out, path); + + return out; +} + +static GString * +meta_journal_entry_finish (GString *out) +{ + guint32 len; + + while (out->len % 4 != 0) + g_string_append_c (out, 0); + + len = out->len + 4; + append_uint32 (out, len); + set_uint32 (out, 0, len); + set_uint32 (out, 4, metadata_crc32 (out->str + 8, len - 8)); + return out; +} + +static GString * +meta_journal_entry_new_set (guint64 mtime, + const char *path, + const char *key, + const char *value) +{ + GString *out; + + out = meta_journal_entry_init (JOURNAL_OP_SET_KEY, mtime, path); + append_string (out, key); + append_string (out, value); + return meta_journal_entry_finish (out); +} + +static GString * +meta_journal_entry_new_setv (guint64 mtime, + const char *path, + const char *key, + char **value) +{ + GString *out; + int i; + + out = meta_journal_entry_init (JOURNAL_OP_SETV_KEY, mtime, path); + append_string (out, key); + + /* Pad to 32bit */ + while (out->len % 4 != 0) + g_string_append_c (out, 0); + + append_uint32 (out, g_strv_length ((char **)value)); + for (i = 0; value[i] != NULL; i++) + append_string (out, value[i]); + + return meta_journal_entry_finish (out); +} + +static GString * +meta_journal_entry_new_remove (guint64 mtime, + const char *path) +{ + GString *out; + + out = meta_journal_entry_init (JOURNAL_OP_REMOVE_PATH, mtime, path); + return meta_journal_entry_finish (out); +} + +static GString * +meta_journal_entry_new_copy (guint64 mtime, + const char *src, + const char *dst) +{ + GString *out; + + out = meta_journal_entry_init (JOURNAL_OP_COPY_PATH, mtime, dst); + append_string (out, src); + return meta_journal_entry_finish (out); +} + +static GString * +meta_journal_entry_new_unset (guint64 mtime, + const char *path, + const char *key) +{ + GString *out; + + out = meta_journal_entry_init (JOURNAL_OP_UNSET_KEY, mtime, path); + append_string (out, key); + return meta_journal_entry_finish (out); +} + + +/* Call with writer lock held */ +static gboolean +meta_journal_add_entry (MetaJournal *journal, + GString *entry) +{ + char *ptr; + guint32 offset; + + g_assert (journal->journal_valid); + + ptr = (char *)journal->last_entry; + offset = ptr - journal->data; + + /* Does the entry fit? */ + if (entry->len > journal->len - offset) + return FALSE; + + memcpy (ptr, entry->str, entry->len); + + journal->header->num_entries = GUINT_TO_BE (journal->last_entry_num + 1); + meta_journal_validate_more (journal); + g_assert (journal->journal_valid); + + return TRUE; +} + +static MetaJournal * +meta_journal_open (MetaTree *tree, const char *filename, gboolean for_write, guint32 tag) +{ + MetaJournal *journal; + struct stat statbuf; + int fd; + char *data; + char *journal_filename; + int open_flags, mmap_prot; + gboolean retried; + + g_assert (sizeof (MetaJournalHeader) == 20); + retried = FALSE; + + if (for_write) + open_flags = O_RDWR; + else + open_flags = O_RDONLY; + + retry: + journal_filename = meta_builder_get_journal_filename (filename, tag); + fd = safe_open (tree, journal_filename, open_flags); + g_free (journal_filename); + if (fd == -1) + { + if (errno == ENOENT && tree->for_write && !retried) + { + retried = TRUE; + if (meta_builder_create_new_journal (filename, tag)) + goto retry; + } + return NULL; + } + + if (fstat (fd, &statbuf) != 0 || + statbuf.st_size < sizeof (MetaJournalHeader)) + { + close (fd); + return NULL; + } + + mmap_prot = PROT_READ; + if (for_write) + mmap_prot |= PROT_WRITE; + data = mmap (NULL, statbuf.st_size, mmap_prot, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) + { + close (fd); + return NULL; + } + + journal = g_new0 (MetaJournal, 1); + journal->filename = g_strdup (filename); + journal->fd = fd; + journal->len = statbuf.st_size; + journal->data = data; + journal->header = (MetaJournalHeader *)data; + journal->first_entry = (MetaJournalEntry *)(data + sizeof (MetaJournalHeader)); + journal->last_entry = journal->first_entry; + journal->last_entry_num = 0; + + if (memcmp (journal->header->magic, JOURNAL_MAGIC, JOURNAL_MAGIC_LEN) != 0) + goto err; + + if (journal->header->major != JOURNAL_MAJOR_VERSION) + goto err; + + if (journal->len != GUINT32_FROM_BE (journal->header->file_size)) + goto err; + + if (tag != GUINT32_FROM_BE (journal->header->random_tag)) + goto err; + + journal->journal_valid = TRUE; + meta_journal_validate_more (journal); + + return journal; + + err: + meta_journal_free (journal); + return NULL; +} + +static char * +get_next_arg (char *str) +{ + return str + strlen (str) + 1; +} + +static gboolean +journal_entry_is_key_type (MetaJournalEntry *entry) +{ + return + entry->entry_type == JOURNAL_OP_SET_KEY || + entry->entry_type == JOURNAL_OP_SETV_KEY || + entry->entry_type == JOURNAL_OP_UNSET_KEY; +} + +static gboolean +journal_entry_is_path_type (MetaJournalEntry *entry) +{ + return + entry->entry_type == JOURNAL_OP_COPY_PATH || + entry->entry_type == JOURNAL_OP_REMOVE_PATH; +} + +/* returns remainer if path has "prefix" as prefix (or is equal to prefix) */ +static const char * +get_prefix_match (const char *path, + const char *prefix) +{ + gsize prefix_len; + const char *remainder; + + prefix_len = strlen (prefix); + + /* Handle trailing slashes in prefix, this is not + generally common, but happens in the case of the + root dir "/" */ + while (prefix_len > 0 && + prefix[prefix_len-1] == '/') + prefix_len--; + + if (strncmp (path, prefix, prefix_len) != 0) + return NULL; + + remainder = path + prefix_len; + if (*remainder != 0 && + *remainder != '/') + return NULL; /* only a string prefix, not a path prefix */ + + while (*remainder == '/') + remainder++; + + return remainder; +} + +typedef gboolean (*journal_key_callback) (MetaJournal *journal, + MetaJournalEntryType entry_type, + const char *path, + guint64 mtime, + const char *key, + gpointer value, + char **iter_path, + gpointer user_data); +typedef gboolean (*journal_path_callback) (MetaJournal *journal, + MetaJournalEntryType entry_type, + const char *path, + guint64 mtime, + const char *source_path, + char **iter_path, + gpointer user_data); + +static char * +meta_journal_iterate (MetaJournal *journal, + const char *path, + journal_key_callback key_callback, + journal_path_callback path_callback, + gpointer user_data) +{ + MetaJournalEntry *entry; + guint32 *sizep, size; + char *journal_path, *journal_key, *source_path; + char *path_copy, *value; + gboolean res; + guint64 mtime; + + path_copy = g_strdup (path); + + if (journal == NULL) + return path_copy; + + entry = journal->last_entry; + while (entry > journal->first_entry) + { + sizep = (guint32 *)entry; + size = GUINT32_FROM_BE (*(sizep-1)); + entry = (MetaJournalEntry *)((char *)entry - size); + if (size < sizeof (MetaJournalEntry) || + entry < journal->first_entry || + entry >= journal->last_entry) + { + g_warning ("meta_journal_iterate: found wrong sized entry, possible journal corruption\n"); + break; + } + + mtime = GUINT64_FROM_BE (ldq_u (&(entry->mtime))); + journal_path = &entry->path[0]; + + if (journal_entry_is_key_type (entry) && + key_callback) /* set, setv or unset */ + { + journal_key = get_next_arg (journal_path); + value = get_next_arg (journal_key); + + /* Only affects is path is exactly the same */ + res = key_callback (journal, entry->entry_type, + journal_path, mtime, journal_key, + value, + &path_copy, user_data); + if (!res) + { + g_free (path_copy); + return NULL; + } + } + else if (journal_entry_is_path_type (entry) && + path_callback) /* copy or remove */ + { + source_path = NULL; + if (entry->entry_type == JOURNAL_OP_COPY_PATH) + source_path = get_next_arg (journal_path); + + res = path_callback (journal, entry->entry_type, + journal_path, mtime, source_path, + &path_copy, user_data); + if (!res) + { + g_free (path_copy); + return NULL; + } + } + else + g_warning ("Unknown journal entry type %d\n", entry->entry_type); + } + + return path_copy; +} + +typedef struct { + const char *key; + MetaKeyType type; + guint64 mtime; + gpointer value; +} PathKeyData; + +static gboolean +journal_iter_key (MetaJournal *journal, + MetaJournalEntryType entry_type, + const char *path, + guint64 mtime, + const char *key, + gpointer value, + char **iter_path, + gpointer user_data) +{ + PathKeyData *data = user_data; + + if (strcmp (path, *iter_path) != 0) + return TRUE; /* No match, continue */ + + data->mtime = mtime; + + if (data->key == NULL) + return FALSE; /* Matched path, not interested in key, stop iterating */ + + if (strcmp (data->key, key) != 0) + return TRUE; /* No match, continue */ + + switch (entry_type) + { + case JOURNAL_OP_SET_KEY: + data->type = META_KEY_TYPE_STRING; + data->value = value; + break; + case JOURNAL_OP_SETV_KEY: + data->type = META_KEY_TYPE_STRINGV; + data->value = value; + break; + case JOURNAL_OP_UNSET_KEY: + data->type = META_KEY_TYPE_NONE; + data->value = NULL; + break; + default: + /* No other key type should reach this */ + g_assert_not_reached (); + } + return FALSE; /* stop iterating */ +} + +static gboolean +journal_iter_path (MetaJournal *journal, + MetaJournalEntryType entry_type, + const char *path, + guint64 mtime, + const char *source_path, + char **iter_path, + gpointer user_data) +{ + PathKeyData *data = user_data; + char *old_path; + const char *remainder; + + /* is this a parent of the iter path */ + remainder = get_prefix_match (*iter_path, path); + if (remainder == NULL) + return TRUE; /* Not related, continue */ + + /* path is affected as a child of this node */ + if (entry_type == JOURNAL_OP_REMOVE_PATH) + { + if (data) + { + data->mtime = mtime; + data->type = META_KEY_TYPE_NONE; + data->value = NULL; + } + return FALSE; /* stop iterating */ + } + else if (entry_type == JOURNAL_OP_COPY_PATH) + { + old_path = *iter_path; + *iter_path = g_build_filename (source_path, remainder, NULL); + g_free (old_path); + return TRUE; /* Continue, with new path */ + } + return TRUE; +} + +static char * +meta_journal_reverse_map_path_and_key (MetaJournal *journal, + const char *path, + const char *key, + MetaKeyType *type, + guint64 *mtime, + gpointer *value) +{ + PathKeyData data = {NULL}; + char *res_path; + + data.key = key; + res_path = meta_journal_iterate (journal, + path, + journal_iter_key, + journal_iter_path, + &data); + *type = data.type; + if (mtime) + *mtime = data.mtime; + *value = data.value; + return res_path; +} + +MetaKeyType +meta_tree_lookup_key_type (MetaTree *tree, + const char *path, + const char *key) +{ + MetaFileData *data; + MetaFileDataEnt *ent; + char *new_path; + MetaKeyType type; + gpointer value; + + g_rw_lock_reader_lock (&metatree_lock); + + new_path = meta_journal_reverse_map_path_and_key (tree->journal, + path, + key, + &type, NULL, &value); + if (new_path == NULL) + goto out; /* type is set */ + + data = meta_tree_lookup_data (tree, new_path); + ent = NULL; + if (data) + ent = meta_data_get_key (tree, data, key); + + g_free (new_path); + + if (ent == NULL) + type = META_KEY_TYPE_NONE; + else if (GUINT32_FROM_BE (ent->key) & KEY_IS_LIST_MASK) + type = META_KEY_TYPE_STRINGV; + else + type = META_KEY_TYPE_STRING; + + out: + g_rw_lock_reader_unlock (&metatree_lock); + return type; +} + +guint64 +meta_tree_get_last_changed (MetaTree *tree, + const char *path) +{ + MetaFileDirEnt *dirent; + MetaKeyType type; + char *new_path; + gpointer value; + guint64 res, mtime; + + g_rw_lock_reader_lock (&metatree_lock); + + new_path = meta_journal_reverse_map_path_and_key (tree->journal, + path, + NULL, + &type, &mtime, &value); + if (new_path == NULL) + { + res = mtime; + goto out; + } + + res = 0; + dirent = meta_tree_lookup (tree, new_path); + if (dirent) + res = get_time_t (tree, dirent->last_changed); + + g_free (new_path); + + out: + g_rw_lock_reader_unlock (&metatree_lock); + + return res; +} + +char * +meta_tree_lookup_string (MetaTree *tree, + const char *path, + const char *key) +{ + MetaFileData *data; + MetaFileDataEnt *ent; + MetaKeyType type; + gpointer value; + char *new_path; + char *res; + + g_rw_lock_reader_lock (&metatree_lock); + + new_path = meta_journal_reverse_map_path_and_key (tree->journal, + path, + key, + &type, NULL, &value); + if (new_path == NULL) + { + res = NULL; + if (type == META_KEY_TYPE_STRING) + res = g_strdup (value); + goto out; + } + + data = meta_tree_lookup_data (tree, new_path); + ent = NULL; + if (data) + ent = meta_data_get_key (tree, data, key); + + g_free (new_path); + + if (ent == NULL) + res = NULL; + else if (GUINT32_FROM_BE (ent->key) & KEY_IS_LIST_MASK) + res = NULL; + else + res = g_strdup (verify_string (tree, ent->value)); + + out: + g_rw_lock_reader_unlock (&metatree_lock); + + return res; +} + +static char ** +get_stringv_from_journal (gpointer value, + gboolean dup_strings) +{ + char *valuep = value; + guint32 num_strings, i; + char **res; + + while (((gsize)valuep) % 4 != 0) + valuep++; + + num_strings = GUINT32_FROM_BE (*(guint32 *)valuep); + valuep += 4; + + res = g_new (char *, num_strings + 1); + + for (i = 0; i < num_strings; i++) + { + if (dup_strings) + res[i] = g_strdup (valuep); + else + res[i] = valuep; + valuep = get_next_arg (valuep); + } + + res[i] = NULL; + + return res; +} + +char ** +meta_tree_lookup_stringv (MetaTree *tree, + const char *path, + const char *key) +{ + MetaFileData *data; + MetaFileDataEnt *ent; + MetaKeyType type; + MetaFileStringv *stringv; + gpointer value; + char *new_path; + char **res; + guint32 num_strings, i; + + g_rw_lock_reader_lock (&metatree_lock); + + new_path = meta_journal_reverse_map_path_and_key (tree->journal, + path, + key, + &type, NULL, &value); + if (new_path == NULL) + { + res = NULL; + if (type == META_KEY_TYPE_STRINGV) + res = get_stringv_from_journal (value, TRUE); + goto out; + } + + data = meta_tree_lookup_data (tree, new_path); + ent = NULL; + if (data) + ent = meta_data_get_key (tree, data, key); + + g_free (new_path); + + if (ent == NULL) + res = NULL; + else if ((GUINT32_FROM_BE (ent->key) & KEY_IS_LIST_MASK) == 0) + res = NULL; + else + { + stringv = verify_array_block (tree, ent->value, + sizeof (guint32)); + num_strings = GUINT32_FROM_BE (stringv->num_strings); + res = g_new (char *, num_strings + 1); + for (i = 0; i < num_strings; i++) + res[i] = g_strdup (verify_string (tree, stringv->strings[i])); + res[i] = NULL; + } + + out: + g_rw_lock_reader_unlock (&metatree_lock); + + return res; +} + +typedef struct { + char *name; + guint64 last_changed; + gboolean has_children; + gboolean has_data; + gboolean exists; /* May be true even if deleted is true, if recreated */ + gboolean deleted; /* Was deleted at some point, ignore everything before */ + + gboolean reported; /* Set to true when reported to user */ +} EnumDirChildInfo; + +typedef struct { + GHashTable *children; +} EnumDirData; + + +static void +child_info_free (EnumDirChildInfo *info) +{ + g_free (info->name); + g_free (info); +} + +static EnumDirChildInfo * +get_child_info (EnumDirData *data, + const char *remainder, + gboolean *direct_child) +{ + EnumDirChildInfo *info; + const char *slash; + char *name; + + slash = strchr (remainder, '/'); + if (slash != NULL) + name = g_strndup (remainder, slash - remainder); + else + name = g_strdup (remainder); + + *direct_child = slash == NULL; + + info = g_hash_table_lookup (data->children, name); + if (info == NULL) + { + info = g_new0 (EnumDirChildInfo, 1); + info->name = name; + g_hash_table_insert (data->children, info->name, info); + } + else + g_free (name); + + return info; +} + +static gboolean +enum_dir_iter_key (MetaJournal *journal, + MetaJournalEntryType entry_type, + const char *path, + guint64 mtime, + const char *key, + gpointer value, + char **iter_path, + gpointer user_data) +{ + EnumDirData *data = user_data; + EnumDirChildInfo *info; + gboolean direct_child; + const char *remainder; + + /* is this a true child of iter_path, then that may create a child */ + remainder = get_prefix_match (path, *iter_path); + if (remainder != NULL && *remainder != 0) + { + info = get_child_info (data, remainder, &direct_child); + + if (!info->deleted) + { + info->exists = TRUE; + if (info->last_changed == 0) + info->last_changed = mtime; + info->has_children |= !direct_child; + info->has_data |= + direct_child && entry_type != JOURNAL_OP_UNSET_KEY; + } + } + + return TRUE; /* continue */ +} + +static gboolean +enum_dir_iter_path (MetaJournal *journal, + MetaJournalEntryType entry_type, + const char *path, + guint64 mtime, + const char *source_path, + char **iter_path, + gpointer user_data) +{ + EnumDirData *data = user_data; + EnumDirChildInfo *info; + gboolean direct_child; + const char *remainder; + char *old_path; + + /* Is path a true child of iter_path */ + remainder = get_prefix_match (path, *iter_path); + if (remainder != NULL && *remainder != 0) + { + info = get_child_info (data, remainder, &direct_child); + + /* copy destination a true child, that creates a child */ + if (entry_type == JOURNAL_OP_COPY_PATH) + { + if (!info->deleted) + { + info->exists = TRUE; + if (info->last_changed == 0) + info->last_changed = mtime; + info->has_children = TRUE; + info->has_data = TRUE; + } + } + else if (entry_type == JOURNAL_OP_REMOVE_PATH && + direct_child) + { + info->deleted = TRUE; + } + } + + /* is this a parent of the iter path */ + remainder = get_prefix_match (*iter_path, path); + if (remainder != NULL) + { + /* path is affected as a child of this node */ + if (entry_type == JOURNAL_OP_REMOVE_PATH) + return FALSE; /* stop iterating */ + else if (entry_type == JOURNAL_OP_COPY_PATH) + { + old_path = *iter_path; + *iter_path = g_build_filename (source_path, remainder, NULL); + g_free (old_path); + return TRUE; /* Continue, with new path */ + } + } + + return TRUE; +} + +static gboolean +enumerate_dir (MetaTree *tree, + MetaFileDir *dir, + GHashTable *children, + meta_tree_dir_enumerate_callback callback, + gpointer user_data) +{ + guint32 i, num_children; + MetaFileDirEnt *dirent; + EnumDirChildInfo *info; + char *dirent_name; + gboolean has_children; + gboolean has_data; + guint64 last_changed; + + num_children = GUINT32_FROM_BE (dir->num_children); + for (i = 0; i < num_children; i++) + { + dirent = &dir->children[i]; + dirent_name = verify_string (tree, dirent->name); + if (dirent_name == NULL) + continue; + + last_changed = get_time_t (tree, dirent->last_changed); + has_children = dirent->children != 0; + has_data = dirent->metadata != 0; + + info = g_hash_table_lookup (children, dirent_name); + if (info) + { + if (info->deleted) + continue; /* if recreated (i.e. exists == TRUE), report later */ + + info->reported = TRUE; + + if (info->last_changed != 0) + last_changed = MAX (last_changed, info->last_changed); + + has_children |= info->has_children; + has_data |= info->has_data; + } + + if (!callback (dirent_name, + last_changed, + has_children, + has_data, + user_data)) + return FALSE; + } + return TRUE; +} + +void +meta_tree_enumerate_dir (MetaTree *tree, + const char *path, + meta_tree_dir_enumerate_callback callback, + gpointer user_data) +{ + EnumDirData data; + GHashTable *children; + EnumDirChildInfo *info; + MetaFileDirEnt *dirent; + GHashTableIter iter; + MetaFileDir *dir; + char *res_path; + + g_rw_lock_reader_lock (&metatree_lock); + + data.children = children = + g_hash_table_new_full (g_str_hash, + g_str_equal, + NULL, + (GDestroyNotify)child_info_free); + + + res_path = meta_journal_iterate (tree->journal, + path, + enum_dir_iter_key, + enum_dir_iter_path, + &data); + + if (res_path != NULL) + { + dirent = meta_tree_lookup (tree, res_path); + if (dirent != NULL && + dirent->children != 0) + { + dir = verify_children_block (tree, dirent->children); + if (dir) + { + if (!enumerate_dir (tree, dir, children, callback, user_data)) + goto out; + } + } + } + + g_hash_table_iter_init (&iter, children); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&info)) + { + if (info->reported || !info->exists) + continue; + + if (!callback (info->name, + info->last_changed, + info->has_children, + info->has_data, + user_data)) + break; + } + out: + g_free (res_path); + g_hash_table_destroy (children); + g_rw_lock_reader_unlock (&metatree_lock); +} + +typedef struct { + char *key; + + MetaKeyType type; + gpointer value; + + gboolean seen; /* We saw this key in the journal */ +} EnumKeysInfo; + +typedef struct { + GHashTable *keys; +} EnumKeysData; + + +static void +key_info_free (EnumKeysInfo *info) +{ + g_free (info->key); + g_free (info); +} + +static EnumKeysInfo * +get_key_info (EnumKeysData *data, + const char *key) +{ + EnumKeysInfo *info; + + info = g_hash_table_lookup (data->keys, key); + if (info == NULL) + { + info = g_new0 (EnumKeysInfo, 1); + info->key = g_strdup (key); + g_hash_table_insert (data->keys, info->key, info); + } + + return info; +} + +static gboolean +enum_keys_iter_key (MetaJournal *journal, + MetaJournalEntryType entry_type, + const char *path, + guint64 mtime, + const char *key, + gpointer value, + char **iter_path, + gpointer user_data) +{ + EnumKeysData *data = user_data; + EnumKeysInfo *info; + + if (strcmp (path, *iter_path) == 0) + { + info = get_key_info (data, key); + + if (!info->seen) + { + info->seen = TRUE; + if (entry_type == JOURNAL_OP_UNSET_KEY) + info->type = META_KEY_TYPE_NONE; + else if (entry_type == JOURNAL_OP_SET_KEY) + info->type = META_KEY_TYPE_STRING; + else + info->type = META_KEY_TYPE_STRINGV; + info->value = value; + } + } + + return TRUE; /* continue */ +} + +static gboolean +enum_keys_iter_path (MetaJournal *journal, + MetaJournalEntryType entry_type, + const char *path, + guint64 mtime, + const char *source_path, + char **iter_path, + gpointer user_data) +{ + const char *remainder; + char *old_path; + + /* is this a parent of the iter path */ + remainder = get_prefix_match (*iter_path, path); + if (remainder != NULL) + { + /* path is affected as a child of this node */ + if (entry_type == JOURNAL_OP_REMOVE_PATH) + return FALSE; /* stop iterating */ + else if (entry_type == JOURNAL_OP_COPY_PATH) + { + old_path = *iter_path; + *iter_path = g_build_filename (source_path, remainder, NULL); + g_free (old_path); + return TRUE; /* Continue, with new path */ + } + } + + return TRUE; +} + +static gboolean +enumerate_data (MetaTree *tree, + MetaFileData *data, + GHashTable *keys, + meta_tree_keys_enumerate_callback callback, + gpointer user_data) +{ + guint32 i, j, num_keys, num_strings; + MetaFileDataEnt *ent; + EnumKeysInfo *info; + char *key_name; + guint32 key_id; + MetaKeyType type; + gpointer value; + MetaFileStringv *stringv; + gpointer free_me; + char **strv; + char *strv_static[10]; + + num_keys = GUINT32_FROM_BE (data->num_keys); + for (i = 0; i < num_keys; i++) + { + ent = &data->keys[i]; + + key_id = GUINT32_FROM_BE (ent->key) & ~KEY_IS_LIST_MASK; + if (GUINT32_FROM_BE (ent->key) & KEY_IS_LIST_MASK) + type = META_KEY_TYPE_STRINGV; + else + type = META_KEY_TYPE_STRING; + + if (key_id >= tree->num_attributes) + continue; + + key_name = tree->attributes[key_id]; + if (key_name == NULL) + continue; + + info = g_hash_table_lookup (keys, key_name); + if (info) + continue; /* overridden, handle later */ + + free_me = NULL; + if (type == META_KEY_TYPE_STRING) + value = verify_string (tree, ent->value); + else + { + stringv = verify_array_block (tree, ent->value, + sizeof (guint32)); + num_strings = GUINT32_FROM_BE (stringv->num_strings); + + if (num_strings < 10) + strv = strv_static; + else + { + strv = g_new (char *, num_strings + 1); + free_me = (gpointer)strv; + } + + for (j = 0; j < num_strings; j++) + strv[j] = verify_string (tree, stringv->strings[j]); + strv[j] = NULL; + + value = strv; + } + + if (!callback (key_name, + type, + value, + user_data)) + return FALSE; + + g_free (free_me); + } + return TRUE; +} + +void +meta_tree_enumerate_keys (MetaTree *tree, + const char *path, + meta_tree_keys_enumerate_callback callback, + gpointer user_data) +{ + EnumKeysData keydata; + GHashTable *keys; + EnumKeysInfo *info; + MetaFileData *data; + GHashTableIter iter; + char *res_path; + + g_rw_lock_reader_lock (&metatree_lock); + + keydata.keys = keys = + g_hash_table_new_full (g_str_hash, + g_str_equal, + NULL, + (GDestroyNotify)key_info_free); + + + res_path = meta_journal_iterate (tree->journal, + path, + enum_keys_iter_key, + enum_keys_iter_path, + &keydata); + + if (res_path != NULL) + { + data = meta_tree_lookup_data (tree, res_path); + if (data != NULL) + { + if (!enumerate_data (tree, data, keys, callback, user_data)) + goto out; + } + } + + g_hash_table_iter_init (&iter, keys); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&info)) + { + gpointer value; + + if (info->type == META_KEY_TYPE_NONE) + continue; + + if (info->type == META_KEY_TYPE_STRING) + value = info->value; + else + { + g_assert (info->type == META_KEY_TYPE_STRINGV); + value = get_stringv_from_journal (info->value, FALSE); + } + + if (!callback (info->key, + info->type, + value, + user_data)) + break; + + if (info->type == META_KEY_TYPE_STRINGV) + g_free (value); + + } + out: + g_free (res_path); + g_hash_table_destroy (keys); + g_rw_lock_reader_unlock (&metatree_lock); +} + + +static void +copy_tree_to_builder (MetaTree *tree, + MetaFileDirEnt *dirent, + MetaFile *builder_file) +{ + MetaFile *builder_child; + MetaFileData *data; + MetaFileDataEnt *ent; + MetaFileDir *dir; + MetaFileDirEnt *child_dirent; + MetaKeyType type; + char *child_name, *key_name, *value; + guint32 i, num_keys, num_children, j; + guint32 key_id; + + /* Copy metadata */ + data = verify_metadata_block (tree, dirent->metadata); + if (data) + { + num_keys = GUINT32_FROM_BE (data->num_keys); + for (i = 0; i < num_keys; i++) + { + ent = &data->keys[i]; + + key_id = GUINT32_FROM_BE (ent->key) & ~KEY_IS_LIST_MASK; + if (GUINT32_FROM_BE (ent->key) & KEY_IS_LIST_MASK) + type = META_KEY_TYPE_STRINGV; + else + type = META_KEY_TYPE_STRING; + + if (key_id >= tree->num_attributes) + continue; + + key_name = tree->attributes[key_id]; + if (key_name == NULL) + continue; + + if (type == META_KEY_TYPE_STRING) + { + value = verify_string (tree, ent->value); + if (value) + metafile_key_set_value (builder_file, + key_name, value); + } + else + { + MetaFileStringv *stringv; + guint32 num_strings; + char *str; + + stringv = verify_array_block (tree, ent->value, + sizeof (guint32)); + + if (stringv) + { + metafile_key_list_set (builder_file, key_name); + + num_strings = GUINT32_FROM_BE (stringv->num_strings); + for (j = 0; j < num_strings; j++) + { + str = verify_string (tree, stringv->strings[j]); + if (str) + metafile_key_list_add (builder_file, + key_name, str); + } + } + } + } + } + + /* Copy last changed time */ + builder_file->last_changed = get_time_t (tree, dirent->last_changed); + + /* Copy children */ + if (dirent->children != 0 && + (dir = verify_children_block (tree, dirent->children)) != NULL) + { + num_children = GUINT32_FROM_BE (dir->num_children); + for (i = 0; i < num_children; i++) + { + child_dirent = &dir->children[i]; + child_name = verify_string (tree, child_dirent->name); + if (child_name != NULL) + { + builder_child = metafile_new (child_name, builder_file); + copy_tree_to_builder (tree, child_dirent, builder_child); + } + } + } +} + +static void +apply_journal_to_builder (MetaTree *tree, + MetaBuilder *builder) +{ + MetaJournal *journal; + MetaJournalEntry *entry; + guint32 *sizep; + guint64 mtime; + char *journal_path, *journal_key, *source_path; + char *value; + char **strv; + MetaFile *file; + int i; + + journal = tree->journal; + + entry = journal->first_entry; + while (entry < journal->last_entry) + { + mtime = GUINT64_FROM_BE (ldq_u (&(entry->mtime))); + journal_path = &entry->path[0]; + + switch (entry->entry_type) + { + case JOURNAL_OP_SET_KEY: + journal_key = get_next_arg (journal_path); + value = get_next_arg (journal_key); + file = meta_builder_lookup (builder, journal_path, TRUE); + metafile_key_set_value (file, + journal_key, + value); + metafile_set_mtime (file, mtime); + break; + case JOURNAL_OP_SETV_KEY: + journal_key = get_next_arg (journal_path); + value = get_next_arg (journal_key); + strv = get_stringv_from_journal (value, FALSE); + file = meta_builder_lookup (builder, journal_path, TRUE); + + metafile_key_list_set (file, journal_key); + for (i = 0; strv[i] != NULL; i++) + metafile_key_list_add (file, journal_key, strv[i]); + + g_free (strv); + metafile_set_mtime (file, mtime); + break; + case JOURNAL_OP_UNSET_KEY: + journal_key = get_next_arg (journal_path); + file = meta_builder_lookup (builder, journal_path, FALSE); + if (file) + { + metafile_key_unset (file, journal_key); + metafile_set_mtime (file, mtime); + } + break; + case JOURNAL_OP_COPY_PATH: + source_path = get_next_arg (journal_path); + meta_builder_copy (builder, + source_path, + journal_path, + mtime); + break; + case JOURNAL_OP_REMOVE_PATH: + meta_builder_remove (builder, + journal_path, + mtime); + break; + default: + break; + } + + sizep = (guint32 *)entry; + entry = (MetaJournalEntry *)((char *)entry + GUINT32_FROM_BE (*(sizep))); + if (GUINT32_FROM_BE (*(sizep)) < sizeof (MetaJournalEntry) || + entry < journal->first_entry || + entry > journal->last_entry) + { + /* This shouldn't happen, we found an entry that is shorter than its data */ + /* See https://bugzilla.gnome.org/show_bug.cgi?id=637095 for discussion */ + g_warning ("apply_journal_to_builder: found wrong sized entry, possible journal corruption\n"); + break; + } + } +} + + +/* Needs write lock */ +static gboolean +meta_tree_flush_locked (MetaTree *tree) +{ + MetaBuilder *builder; + gboolean res; + + builder = meta_builder_new (); + + if (tree->root) + { + copy_tree_to_builder (tree, tree->root, builder->root); + } + else + { + /* It shouldn't happen, because tree is recovered in case of failed write + * out. Skip copy_tree_to_builder to avoid crash. */ + g_warning ("meta_tree_flush_locked: tree->root == NULL, possible data loss"); + } + + if (tree->journal) + apply_journal_to_builder (tree, builder); + + res = meta_builder_write (builder, + meta_tree_get_filename (tree)); + if (res) + { + /* Force re-read since we wrote a new file */ + res = meta_tree_refresh_locked (tree, TRUE); + + if (tree->root == NULL) + { + /* It shouldn't happen. We failed to write out an updated tree + * probably, therefore all the data are lost. Backup the file and + * reload the tree to avoid further crashes. */ + GDateTime *dt; + char *timestamp, *backup; + + dt = g_date_time_new_now_local (); + timestamp = g_date_time_format_iso8601 (dt); + backup = g_strconcat (meta_tree_get_filename (tree), ".backup.", + timestamp, NULL); + g_rename (meta_tree_get_filename (tree), backup); + + g_warning ("meta_tree_flush_locked: tree->root == NULL, possible data loss\n" + "corrupted file was moved to: %s\n" + "(please make a comment on https://bugzilla.gnome.org/show_bug.cgi?id=598561 " + "and attach the corrupted file)", + backup); + + g_free (timestamp); + g_free (backup); + g_date_time_unref (dt); + + res = meta_tree_refresh_locked (tree, TRUE); + g_assert (res); + } + } + + meta_builder_free (builder); + + return res; +} + +/* NB: The tree can be uninitialized if FALSE is returned! */ +gboolean +meta_tree_flush (MetaTree *tree) +{ + gboolean res; + + g_rw_lock_writer_lock (&metatree_lock); + res = meta_tree_flush_locked (tree); + g_rw_lock_writer_unlock (&metatree_lock); + return res; +} + +gboolean +meta_tree_unset (MetaTree *tree, + const char *path, + const char *key) +{ + GString *entry; + guint64 mtime; + gboolean res; + + g_rw_lock_writer_lock (&metatree_lock); + + if (tree->journal == NULL || + !tree->journal->journal_valid) + { + res = FALSE; + goto out; + } + + mtime = time (NULL); + + entry = meta_journal_entry_new_unset (mtime, path, key); + + res = TRUE; + if (!meta_journal_add_entry (tree->journal, entry)) + { + if (meta_tree_flush_locked (tree)) + { + if (!meta_journal_add_entry (tree->journal, entry)) + { + g_warning ("meta_tree_unset: entry is bigger then the size of journal\n"); + res = FALSE; + } + } + else + res = FALSE; + } + + g_string_free (entry, TRUE); + + out: + g_rw_lock_writer_unlock (&metatree_lock); + return res; +} + +gboolean +meta_tree_set_string (MetaTree *tree, + const char *path, + const char *key, + const char *value) +{ + GString *entry; + guint64 mtime; + gboolean res; + + g_rw_lock_writer_lock (&metatree_lock); + + if (tree->journal == NULL || + !tree->journal->journal_valid) + { + res = FALSE; + goto out; + } + + mtime = time (NULL); + + entry = meta_journal_entry_new_set (mtime, path, key, value); + + res = TRUE; + if (!meta_journal_add_entry (tree->journal, entry)) + { + if (meta_tree_flush_locked (tree)) + { + if (!meta_journal_add_entry (tree->journal, entry)) + { + g_warning ("meta_tree_set_string: entry is bigger then the size of journal\n"); + res = FALSE; + } + } + else + res = FALSE; + } + + g_string_free (entry, TRUE); + + out: + g_rw_lock_writer_unlock (&metatree_lock); + return res; +} + +gboolean +meta_tree_set_stringv (MetaTree *tree, + const char *path, + const char *key, + char **value) +{ + GString *entry; + guint64 mtime; + gboolean res; + + g_rw_lock_writer_lock (&metatree_lock); + + if (tree->journal == NULL || + !tree->journal->journal_valid) + { + res = FALSE; + goto out; + } + + mtime = time (NULL); + + entry = meta_journal_entry_new_setv (mtime, path, key, value); + + res = TRUE; + if (!meta_journal_add_entry (tree->journal, entry)) + { + if (meta_tree_flush_locked (tree)) + { + if (!meta_journal_add_entry (tree->journal, entry)) + { + g_warning ("meta_tree_set_stringv: entry is bigger then the size of journal\n"); + res = FALSE; + } + } + else + res = FALSE; + } + + g_string_free (entry, TRUE); + + out: + g_rw_lock_writer_unlock (&metatree_lock); + return res; +} + +gboolean +meta_tree_remove (MetaTree *tree, + const char *path) +{ + GString *entry; + guint64 mtime; + gboolean res; + + g_rw_lock_writer_lock (&metatree_lock); + + if (tree->journal == NULL || + !tree->journal->journal_valid) + { + res = FALSE; + goto out; + } + + mtime = time (NULL); + + entry = meta_journal_entry_new_remove (mtime, path); + + res = TRUE; + if (!meta_journal_add_entry (tree->journal, entry)) + { + if (meta_tree_flush_locked (tree)) + { + if (!meta_journal_add_entry (tree->journal, entry)) + { + g_warning ("meta_tree_remove: entry is bigger then the size of journal\n"); + res = FALSE; + } + } + else + res = FALSE; + } + + g_string_free (entry, TRUE); + + out: + g_rw_lock_writer_unlock (&metatree_lock); + return res; +} + +gboolean +meta_tree_copy (MetaTree *tree, + const char *src, + const char *dest) +{ + GString *entry; + guint64 mtime; + gboolean res; + + g_rw_lock_writer_lock (&metatree_lock); + + if (tree->journal == NULL || + !tree->journal->journal_valid) + { + res = FALSE; + goto out; + } + + mtime = time (NULL); + + entry = meta_journal_entry_new_copy (mtime, src, dest); + + res = TRUE; + if (!meta_journal_add_entry (tree->journal, entry)) + { + if (meta_tree_flush_locked (tree)) + { + if (!meta_journal_add_entry (tree->journal, entry)) + { + g_warning ("meta_tree_copy: entry is bigger then the size of journal\n"); + res = FALSE; + } + } + else + res = FALSE; + } + + g_string_free (entry, TRUE); + + out: + g_rw_lock_writer_unlock (&metatree_lock); + return res; +} + +static char * +canonicalize_filename (const char *filename) +{ + char *canon, *start, *p, *q; + char *cwd; + int i; + + if (!g_path_is_absolute (filename)) + { + cwd = g_get_current_dir (); + canon = g_build_filename (cwd, filename, NULL); + g_free (cwd); + } + else + canon = g_strdup (filename); + + start = (char *)g_path_skip_root (canon); + + if (start == NULL) + { + /* This shouldn't really happen, as g_get_current_dir() should + return an absolute pathname, but bug 573843 shows this is + not always happening */ + g_free (canon); + return g_build_filename (G_DIR_SEPARATOR_S, filename, NULL); + } + + /* POSIX allows double slashes at the start to + * mean something special (as does windows too). + * So, "//" != "/", but more than two slashes + * is treated as "/". + */ + i = 0; + for (p = start - 1; + (p >= canon) && + G_IS_DIR_SEPARATOR (*p); + p--) + i++; + if (i > 2) + { + i -= 1; + start -= i; + memmove (start, start+i, strlen (start+i)+1); + } + + p = start; + while (*p != 0) + { + if (p[0] == '.' && (p[1] == 0 || G_IS_DIR_SEPARATOR (p[1]))) + { + memmove (p, p+1, strlen (p+1)+1); + } + else if (p[0] == '.' && p[1] == '.' && (p[2] == 0 || G_IS_DIR_SEPARATOR (p[2]))) + { + q = p + 2; + /* Skip previous separator */ + p = p - 2; + if (p < start) + p = start; + while (p > start && !G_IS_DIR_SEPARATOR (*p)) + p--; + if (G_IS_DIR_SEPARATOR (*p)) + *p++ = G_DIR_SEPARATOR; + memmove (p, q, strlen (q)+1); + } + else + { + /* Skip until next separator */ + while (*p != 0 && !G_IS_DIR_SEPARATOR (*p)) + p++; + + if (*p != 0) + { + /* Canonicalize one separator */ + *p++ = G_DIR_SEPARATOR; + } + } + + /* Remove additional separators */ + q = p; + while (*q && G_IS_DIR_SEPARATOR (*q)) + q++; + + if (p != q) + memmove (p, q, strlen (q)+1); + } + + /* Remove trailing slashes */ + if (p > start && G_IS_DIR_SEPARATOR (*(p-1))) + *(p-1) = 0; + + return canon; +} + +/* Invariant: If link is canonical, so is result */ +static char * +follow_symlink (const char *link) +{ + char *resolved, *canonical, *parent; + char symlink_value[4096]; + ssize_t res; + + res = readlink (link, symlink_value, sizeof (symlink_value) - 1); + + if (res == -1) + return g_strdup (link); + symlink_value[res] = 0; + + if (g_path_is_absolute (symlink_value)) + return canonicalize_filename (symlink_value); + else + { + parent = g_path_get_dirname (link); + resolved = g_build_filename (parent, symlink_value, NULL); + g_free (parent); + + canonical = canonicalize_filename (resolved); + + g_free (resolved); + + return canonical; + } +} + +/* Returns parent path or NULL if none */ +static char * +get_dirname (const char *path) +{ + char *parent; + + parent = g_path_get_dirname (path); + if (strcmp (parent, ".") == 0 || + strcmp (parent, path) == 0) + { + g_free (parent); + return NULL; + } + + return parent; +} + +/* Invariant: If path is canonical, so is result */ +static void +follow_symlink_recursively (char **path, + dev_t *path_dev) +{ + char *tmp; + struct stat path_stat; + int num_recursions; + + num_recursions = 0; + do { + if (g_lstat (*path, &path_stat) != 0) + { + *path_dev = 0; + return; + } + + if (S_ISLNK (path_stat.st_mode)) + { + tmp = *path; + *path = follow_symlink (*path); + g_free (tmp); + } + + num_recursions++; + if (num_recursions > 12) + break; + } while (S_ISLNK (path_stat.st_mode)); + + *path_dev = path_stat.st_dev; +} + +struct _MetaLookupCache { + char *last_parent; + char *last_parent_expanded; + dev_t last_parent_dev; + char *last_parent_mountpoint; + char *last_parent_mountpoint_extra_prefix; + + dev_t last_device; + char *last_device_tree; +}; + + +static const char * +get_tree_for_device (MetaLookupCache *cache, + dev_t device) +{ + return NULL; +} + + +#ifdef __linux__ + +typedef struct { + char *mountpoint; + char *root; +} MountinfoEntry; + +static gboolean mountinfo_initialized = FALSE; +static int mountinfo_fd = -1; +static MountinfoEntry *mountinfo_roots = NULL; +G_LOCK_DEFINE_STATIC (mountinfo); + +/* We want to avoid mmap and stat as these are not ideal + operations for a proc file */ +static char * +read_contents (int fd) +{ + char *data; + gsize len; + gsize bytes_read; + + len = 4096; + data = g_malloc (len); + + bytes_read = 0; + while (1) + { + gssize rc; + + if (len - bytes_read < 100) + { + len = len + 4096; + data = g_realloc (data, len); + } + + rc = read (fd, data + bytes_read, + len - bytes_read); + if (rc < 0) + { + if (errno != EINTR) + { + g_free (data); + return NULL; + } + } + else if (rc == 0) + break; + else + bytes_read += rc; + } + + /* zero terminate */ + if (len - bytes_read < 1) + data = g_realloc (data, bytes_read + 1); + data[bytes_read] = 0; + + return (char *)data; +} + +static char * +mountinfo_unescape (const char *escaped) +{ + char *res, *s; + char c; + gsize len; + + s = strchr (escaped, ' '); + if (s) + len = s - escaped; + else + len = strlen (escaped); + res = malloc (len + 1); + s = res; + + while (*escaped != 0 && *escaped != ' ') + { + if (*escaped == '\\') + { + escaped++; + c = *escaped++ - '0'; + c <<= 3; + c |= *escaped++ - '0'; + c <<= 3; + c |= *escaped++ - '0'; + } + else + c = *escaped++; + *s++ = c; + } + *s = 0; + return res; +} + +static MountinfoEntry * +parse_mountinfo (const char *contents) +{ + GArray *a; + const char *line; + const char *line_root; + const char *line_mountpoint; + + a = g_array_new (TRUE, TRUE, sizeof (MountinfoEntry)); + + line = contents; + while (line != NULL && *line != 0) + { + /* parent id */ + line = strchr (line, ' '); + line_mountpoint = NULL; + if (line) + { + /* major:minor */ + line = strchr (line+1, ' '); + if (line) + { + /* root */ + line = strchr (line+1, ' '); + line_root = line + 1; + if (line) + { + /* mountpoint */ + line = strchr (line+1, ' '); + line_mountpoint = line + 1; + } + } + } + + if (line_mountpoint && !(line_root[0] == '/' && line_root[1] == ' ')) + { + MountinfoEntry new_entry; + + new_entry.mountpoint = mountinfo_unescape (line_mountpoint); + new_entry.root = mountinfo_unescape (line_root); + + g_array_append_val (a, new_entry); + } + + if (line) + line = strchr (line, '\n'); + if (line) + line++; + } + + return (MountinfoEntry *)g_array_free (a, FALSE); +} + +static void +free_mountinfo (void) +{ + int i; + + if (mountinfo_roots) + { + for (i = 0; mountinfo_roots[i].mountpoint != NULL; i++) + { + g_free (mountinfo_roots[i].mountpoint); + g_free (mountinfo_roots[i].root); + } + g_free (mountinfo_roots); + mountinfo_roots = NULL; + } +} + +static void +update_mountinfo (void) +{ + char *contents; + int res; + gboolean first; + GPollFD pfd; + + first = FALSE; + if (!mountinfo_initialized) + { + mountinfo_initialized = TRUE; + mountinfo_fd = open ("/proc/self/mountinfo", O_RDONLY); + first = TRUE; + } + + if (mountinfo_fd == -1) + return; + + if (!first) + { + pfd.fd = mountinfo_fd; + pfd.events = G_IO_IN | G_IO_OUT | G_IO_PRI; + pfd.revents = 0; + res = g_poll (&pfd, 1, 0); + if (res == 0) + return; + } + + free_mountinfo (); + contents = read_contents (mountinfo_fd); + lseek (mountinfo_fd, SEEK_SET, 0); + if (contents) + { + mountinfo_roots = parse_mountinfo (contents); + g_free (contents); + } +} + +static char * +find_mountinfo_root_for_mountpoint (const char *mountpoint) +{ + char *res; + int i; + + res = NULL; + + G_LOCK (mountinfo); + + update_mountinfo (); + + if (mountinfo_roots) + { + for (i = 0; mountinfo_roots[i].mountpoint != NULL; i++) + { + if (strcmp (mountinfo_roots[i].mountpoint, mountpoint) == 0) + { + res = g_strdup (mountinfo_roots[i].root); + break; + } + } + } + + G_UNLOCK (mountinfo); + + return res; +} + +#endif + + +static char * +get_extra_prefix_for_mount (const char *mountpoint) +{ +#ifdef __linux__ + return find_mountinfo_root_for_mountpoint (mountpoint); +#endif + return NULL; +} + +static dev_t +get_devnum (const char *path) +{ + struct stat path_stat; + + if (g_lstat (path, &path_stat) != 0) + return 0; + + return path_stat.st_dev; +} + +/* Expands symlinks for parents and look for a mountpoint + * file is symlink expanded and canonical + */ +static const char * +find_mountpoint_for (MetaLookupCache *cache, + const char *file, + dev_t dev, + char **prefix_out) +{ + char *first_dir, *dir, *last; + const char *prefix; + dev_t dir_dev = 0; + + first_dir = get_dirname (file); + if (first_dir == NULL) + { + *prefix_out = g_strdup ("/"); + return "/"; + } + + g_assert (cache->last_parent_expanded != NULL); + g_assert (strcmp (cache->last_parent_expanded, first_dir) == 0); + + if (cache->last_parent_mountpoint != NULL) + goto out; /* Cache hit! */ + + dir = g_strdup (first_dir); + last = g_strdup (file); + while (1) + { + if (dir) + dir_dev = get_devnum (dir); + if (dir == NULL || dev != dir_dev) + { + g_free (dir); + cache->last_parent_mountpoint = last; + cache->last_parent_mountpoint_extra_prefix = get_extra_prefix_for_mount (last); + break; + } + + g_free (last); + last = dir; + dir = get_dirname (last); + } + + out: + g_free (first_dir); + + prefix = file + strlen (cache->last_parent_mountpoint); + if (*prefix == 0) + prefix = "/"; + + if (cache->last_parent_mountpoint_extra_prefix) + *prefix_out = g_build_filename (cache->last_parent_mountpoint_extra_prefix, prefix, NULL); + else + *prefix_out = g_strdup (prefix); + + return cache->last_parent_mountpoint; +} + +/* Resolves all symlinks, including the ones for basename. + * Invariant: If path is canonical, so is result. + */ +static char * +expand_all_symlinks (const char *path, + dev_t *dev_out) +{ + char *parent, *parent_expanded; + char *basename, *res; + dev_t dev; + char *path_copy; + + path_copy = g_strdup (path); + follow_symlink_recursively (&path_copy, &dev); + if (dev_out) + *dev_out = dev; + + parent = get_dirname (path_copy); + if (parent) + { + parent_expanded = expand_all_symlinks (parent, NULL); + basename = g_path_get_basename (path_copy); + res = g_build_filename (parent_expanded, basename, NULL); + g_free (parent_expanded); + g_free (basename); + g_free (parent); + g_free (path_copy); + } + else + res = path_copy; + + return res; +} + +MetaLookupCache * +meta_lookup_cache_new (void) +{ + MetaLookupCache *cache; + + cache = g_new0 (MetaLookupCache, 1); + + return cache; +} + +void +meta_lookup_cache_free (MetaLookupCache *cache) +{ + g_free (cache->last_parent); + g_free (cache->last_parent_expanded); + g_free (cache->last_parent_mountpoint); + g_free (cache->last_parent_mountpoint_extra_prefix); + g_free (cache->last_device_tree); + g_free (cache); +} + +static gboolean +path_has_prefix (const char *path, + const char *prefix) +{ + int prefix_len; + + if (prefix == NULL) + return TRUE; + + prefix_len = strlen (prefix); + + if (strncmp (path, prefix, prefix_len) == 0 && + (prefix_len == 0 || /* empty prefix always matches */ + prefix[prefix_len - 1] == '/' || /* last char in prefix was a /, so it must be in path too */ + path[prefix_len] == 0 || + path[prefix_len] == '/')) + return TRUE; + + return FALSE; +} + +struct HomedirData { + dev_t device; + char *expanded_path; +}; + +static char * +expand_parents (MetaLookupCache *cache, + const char *path, + dev_t *parent_dev_out) +{ + char *parent; + char *basename, *res; + char *path_copy; + dev_t parent_dev; + + path_copy = canonicalize_filename (path); + parent = get_dirname (path_copy); + if (parent == NULL) + { + *parent_dev_out = 0; + return path_copy; + } + + if (cache->last_parent == NULL || + strcmp (cache->last_parent, parent) != 0) + { + g_free (cache->last_parent); + g_free (cache->last_parent_expanded); + cache->last_parent = parent; + cache->last_parent_expanded = expand_all_symlinks (parent, &parent_dev); + cache->last_parent_dev = parent_dev; + g_free (cache->last_parent_mountpoint); + cache->last_parent_mountpoint = NULL; + g_free (cache->last_parent_mountpoint_extra_prefix); + cache->last_parent_mountpoint_extra_prefix = NULL; + } + else + g_free (parent); + + *parent_dev_out = cache->last_parent_dev; + basename = g_path_get_basename (path_copy); + g_free (path_copy); + res = g_build_filename (cache->last_parent_expanded, basename, NULL); + g_free (basename); + + return res; +} + +MetaTree * +meta_lookup_cache_lookup_path (MetaLookupCache *cache, + const char *filename, + guint64 device, + gboolean for_write, + char **tree_path) +{ + const char *mountpoint; + const char *treename; + char *prefix; + char *expanded; + static struct HomedirData homedir_data_storage; + static gsize homedir_datap = 0; + struct HomedirData *homedir_data; + MetaTree *tree; + dev_t parent_dev; + + if (g_once_init_enter (&homedir_datap)) + { + char *e; + struct stat statbuf; + + g_stat (g_get_home_dir(), &statbuf); + homedir_data_storage.device = statbuf.st_dev; + e = canonicalize_filename (g_get_home_dir()); + homedir_data_storage.expanded_path = expand_all_symlinks (e, NULL); + g_free (e); + g_once_init_leave (&homedir_datap, (gsize)&homedir_data_storage); + } + homedir_data = (struct HomedirData *)homedir_datap; + + /* Canonicalized form with all symlinks expanded in parents */ + expanded = expand_parents (cache, filename, &parent_dev); + + if (device == 0) /* Unknown, use same as parent */ + device = parent_dev; + + if (homedir_data->device == device && + path_has_prefix (expanded, homedir_data->expanded_path)) + { + treename = "home"; + prefix = expanded + strlen (homedir_data->expanded_path); + if (*prefix == 0) + prefix = g_strdup ("/"); + else + prefix = g_strdup (prefix); + goto found; + } + + treename = get_tree_for_device (cache, device); + + if (treename) + { + mountpoint = find_mountpoint_for (cache, + expanded, + device, + &prefix); + + if (mountpoint == NULL || + strcmp (mountpoint, "/") == 0) + { + /* Fall back to root */ + g_free (prefix); + treename = NULL; + } + } + + if (!treename) + { + treename = "root"; + prefix = g_strdup (expanded); + } + + found: + g_free (expanded); + tree = meta_tree_lookup_by_name (treename, for_write); + if (tree) + { + *tree_path = prefix; + return tree; + } + + g_free (prefix); + return NULL; +} diff -Nru peony-4.10.0.5/debian/upgrade-utils/desktop-grid/metatree.h peony-4.10.0.5/debian/upgrade-utils/desktop-grid/metatree.h --- peony-4.10.0.5/debian/upgrade-utils/desktop-grid/metatree.h 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/upgrade-utils/desktop-grid/metatree.h 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,108 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2009 Red Hat, Inc. + * + * 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 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. + * + * Author: Alexander Larsson <alexl@redhat.com> + */ + +#ifndef __META_TREE_H__ +#define __META_TREE_H__ + +#include <glib.h> + +typedef struct _MetaTree MetaTree; +typedef struct _MetaLookupCache MetaLookupCache; + +typedef enum { + META_KEY_TYPE_NONE, + META_KEY_TYPE_STRING, + META_KEY_TYPE_STRINGV +} MetaKeyType; + +/* Note: These are called with the read-lock held, so + don't call any MetaTree operations */ +typedef gboolean (*meta_tree_dir_enumerate_callback) (const char *entry, + guint64 last_changed, + gboolean has_children, + gboolean has_data, + gpointer user_data); + +typedef gboolean (*meta_tree_keys_enumerate_callback) (const char *key, + MetaKeyType type, + gpointer value, + gpointer user_data); + +/* MetaLookupCache is not threadsafe */ +MetaLookupCache *meta_lookup_cache_new (void); +void meta_lookup_cache_free (MetaLookupCache *cache); +MetaTree *meta_lookup_cache_lookup_path (MetaLookupCache *cache, + const char *filename, + guint64 device, + gboolean for_write, + char **tree_path); + +/* All public MetaTree calls are threadsafe */ +MetaTree * meta_tree_open (const char *filename, + gboolean for_write); +MetaTree * meta_tree_lookup_by_name (const char *name, + gboolean for_write); +MetaTree * meta_tree_ref (MetaTree *tree); +void meta_tree_unref (MetaTree *tree); +gboolean meta_tree_refresh (MetaTree *tree); +const char *meta_tree_get_filename (MetaTree *tree); +gboolean meta_tree_exists (MetaTree *tree); +gboolean meta_tree_is_on_nfs (MetaTree *tree); + +MetaKeyType meta_tree_lookup_key_type (MetaTree *tree, + const char *path, + const char *key); +guint64 meta_tree_get_last_changed (MetaTree *tree, + const char *path); +char * meta_tree_lookup_string (MetaTree *tree, + const char *path, + const char *key); +char ** meta_tree_lookup_stringv (MetaTree *tree, + const char *path, + const char *key); +void meta_tree_enumerate_dir (MetaTree *tree, + const char *path, + meta_tree_dir_enumerate_callback callback, + gpointer user_data); +void meta_tree_enumerate_keys (MetaTree *tree, + const char *path, + meta_tree_keys_enumerate_callback callback, + gpointer user_data); +gboolean meta_tree_flush (MetaTree *tree); +gboolean meta_tree_unset (MetaTree *tree, + const char *path, + const char *key); +gboolean meta_tree_set_string (MetaTree *tree, + const char *path, + const char *key, + const char *value); +gboolean meta_tree_set_stringv (MetaTree *tree, + const char *path, + const char *key, + char **value); +gboolean meta_tree_remove (MetaTree *tree, + const char *path); +gboolean meta_tree_copy (MetaTree *tree, + const char *src, + const char *dest); + +#endif /* __META_TREE_H__ */ diff -Nru peony-4.10.0.5/debian/upgrade-utils/desktop-grid/update-desktop-grid peony-4.10.0.5/debian/upgrade-utils/desktop-grid/update-desktop-grid --- peony-4.10.0.5/debian/upgrade-utils/desktop-grid/update-desktop-grid 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/upgrade-utils/desktop-grid/update-desktop-grid 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,128 @@ +#!/bin/bash + +#获取meta-get命令的位置 +META_GET_COMMAND="/usr/bin/meta-get" + +#获取meta-conf-parser的位置 +META_CONF_PARSER_COMMAND="/usr/bin/meta-conf-parser" + +#打印坐标 +print_poses() { + y=$1 + x=$2 + echo x=$x + echo y=$y +} + +#获取计算机目录的位置,fixme:判断计算机是否隐藏 +get_computer_position() { + user=$1 + #fixme + poses=$(sudo -u ${user} ${META_GET_COMMAND} -t computer: / peony-qt-desktop-item-position) + echo "[computer:///]" + print_poses $poses +} + +#获取回收站目录的位置,fixme:判断回收站是否隐藏 +get_trash_position() { + user=$1 + #fixme + poses=$(sudo -u ${user} ${META_GET_COMMAND} -t trash: / peony-qt-desktop-item-position) + echo "[trash:///]" + print_poses $poses +} + +#获取家目录的位置 +get_home_position() { + user=$1 + #fixme + poses=$(sudo -u ${user} ${META_GET_COMMAND} -t home / peony-qt-desktop-item-position) + echo "[$home_dir]" + print_poses $poses +} + +#获取桌面上单个文件的位置 +get_desktop_file_position() { + user=$1 + home_dir=$2 + child_path=$3 + relative_path=$(echo "$child_path" | sed "s|$home_dir||") + #fixme 带空格的路径位置信息获取,判断是否为隐藏文件,快捷方式(尤其是从其它挂载点链接过来的文件)信息获取 + poses=$(sudo -u ${user} ${META_GET_COMMAND} -t home "${relative_path}" peony-qt-desktop-item-position) + echo "[$child_path]" + print_poses $poses +} + +#获取桌面上所有文件的位置 +get_desktop_files_position() { + #fixme 桌面路径带中文的情况无法正常获取文件信息 + #fixme 桌面路径自定义场景需要处理metadata获取 + user=$1 + desktop_dir=$2 + home_dir=$3 + relative_path=$(echo "$desktop_dir" | sed "s|$home_dir||") + if [ -n "relative_path" ]; then + #echo "桌面相对于家目录的路径 ${relative_path}" + for child in $desktop_dir/* + do + get_desktop_file_position $user "$home_dir" "$child" + done + else + echo "目前不支持自定义桌面路径网格计算" + fi +} + +#用户桌面图标网格计算 +calculate_user_desktop_grid() { + user=$1 + home_dir=$(getent passwd $user | cut -d: -f6) + desktop_dir=$(sudo -u $user xdg-user-dir DESKTOP) + if [ -d "$desktop_dir" ]; then + #echo "计算用户$user的桌面网格" + get_computer_position $user + get_trash_position $user + get_home_position $user $home_dir + get_desktop_files_position $user ${desktop_dir} ${home_dir} + return 0 + else + #echo "用户$user无桌面,略过" + return 1 + fi +} + +current_user=$(whoami) +if [ "$current_user" = "root" ]; then + echo "更新桌面网格配置" +else + echo "需要root用户执行" + exit 1 +fi + +#桌面图标网格重构升级更新前准备 +users=$(getent passwd | cut -d: -f1) + +for user in ${users} +do + home_dir=$(getent passwd $user | cut -d: -f6) + desktop_dir=$(sudo -u $user xdg-user-dir DESKTOP) + #echo "User: $user, Home Path: $home_dir Desktop Path: $desktop_dir" + #获取当前用户的桌面图标数据 + + if [ ${home_dir} == "/dev/null" ]; then + continue + fi + rm -f $home_dir/.peony-desktop-grid.conf + touch $home_dir/.peony-desktop-grid.conf + chmod +rw $home_dir/.peony-desktop-grid.conf + calculate_user_desktop_grid $user > $home_dir/.peony-desktop-grid.conf + chmod -w $home_dir/.peony-desktop-grid.conf + chown $user $home_dir/.peony-desktop-grid.conf + + if [ -z "$(cat "$home_dir/.peony-desktop-grid.conf")" ]; then + rm -f $home_dir/.peony-desktop-grid.conf + else + #使用meta-conf-parser更新网格信息 + echo "更新 $user 桌面图标配置..." + sudo -u $user $META_CONF_PARSER_COMMAND + fi +done diff -Nru peony-4.10.0.5/debian/upgrade-utils/desktop-grid/update-desktop-grid.1 peony-4.10.0.5/debian/upgrade-utils/desktop-grid/update-desktop-grid.1 --- peony-4.10.0.5/debian/upgrade-utils/desktop-grid/update-desktop-grid.1 1970-01-01 08:00:00.000000000 +0800 +++ peony-4.10.0.5/debian/upgrade-utils/desktop-grid/update-desktop-grid.1 2025-03-03 09:26:07.000000000 +0800 @@ -0,0 +1,14 @@ +.\" Man Page for update-desktop-grid.sh +.TH "update-desktop-grid" "1" +.SH "NAME" +update-desktop-grid.sh \- utils for get file meta info. +.SH "SYNOPSIS" +.B update-desktop-grid.sh +.SH "DESCRIPTION" +This program will caculate all usres desktop item position info and convert them to grid position. +.SH "BUGS" +.SS Should you encounter any bugs, they may be reported at: +https://gitee.com/openkylin/peony/issues +.SH "AUTHORS" +.SS This Manual Page has been written for the UKUI Desktop Environment by: +Yue Lan <lanyue@kylinos.cn> (2024)