peony/0000755000175000017500000000000014205115226010626 5ustar fengfengpeony/libpeony-qt/0000755000175000017500000000000014205115226013071 5ustar fengfengpeony/libpeony-qt/complementary-style.h0000644000175000017500000000460514205101223017254 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef COMPLEMENTARYSTYLE_H #define COMPLEMENTARYSTYLE_H #include #include "peony-core_global.h" class QMenu; class QPushButton; namespace Peony { /*! * \brief The ComplementaryStyle class * \details * This class provide a fixed style for painting qt's control * with system theme which qt5-gtk2-platformtheme provided. * * If just use the qpa plugin, there were be some incorrect styled * controls. Such as QToolBarButton's indicator. * * \note * If you are not using gtk-theme as default system theme, you should not use this * proxy style for painting. * * \todo * add border radius support. */ class ComplementaryStyle : public QProxyStyle { Q_OBJECT public: static PEONYCORESHARED_EXPORT ComplementaryStyle *getStyle(); void polish(QWidget *widget); int styleHint(QStyle::StyleHint hint, const QStyleOption *option = nullptr, const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const; void drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const; void drawComplexControl(QStyle::ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p, const QWidget *widget = nullptr) const; private: explicit ComplementaryStyle(QStyle *parent = nullptr); ~ComplementaryStyle(); QMenu *m_styled_menu; QPushButton *m_styled_button; }; } #endif // COMPLEMENTARYSTYLE_H peony/libpeony-qt/libpeony-qt-header.pri0000644000175000017500000000251314205101223017267 0ustar fengfengINCLUDEPATH += $$PWD INCLUDEPATH += $$PWD/file-operation INCLUDEPATH += $$PWD/file-launcher INCLUDEPATH += $$PWD/model INCLUDEPATH += $$PWD/vfs INCLUDEPATH += $$PWD/convenient-utils INCLUDEPATH += $$PWD/controls INCLUDEPATH += $$PWD/windows INCLUDEPATH += $$PWD/effects INCLUDEPATH += $$PWD/controls/directory-view INCLUDEPATH += $$PWD/controls/directory-view/directory-view-factory INCLUDEPATH += $$PWD/controls/directory-view/delegate INCLUDEPATH += $$PWD/controls/directory-view/view INCLUDEPATH += $$PWD/controls/directory-view/view/icon-view INCLUDEPATH += $$PWD/controls/directory-view/view/list-view INCLUDEPATH += $$PWD/controls/menu INCLUDEPATH += $$PWD/controls/menu/directory-view-menu INCLUDEPATH += $$PWD/controls/menu/side-bar-menu INCLUDEPATH += $$PWD/controls/navigation-bar INCLUDEPATH += $$PWD/controls/navigation-bar/location-bar INCLUDEPATH += $$PWD/controls/navigation-bar/path-bar INCLUDEPATH += $$PWD/controls/preview-page INCLUDEPATH += $$PWD/controls/preview-page/default-preview-page INCLUDEPATH += $$PWD/controls/preview-page/preview-page-factory INCLUDEPATH += $$PWD/controls/property-page INCLUDEPATH += $$PWD/controls/side-bar INCLUDEPATH += $$PWD/controls/status-bar INCLUDEPATH += $$PWD/controls/tab-page INCLUDEPATH += $$PWD/controls/tool-bar INCLUDEPATH += $$PWD/effects INCLUDEPATH += $$PWD/../plugin-iface/ peony/libpeony-qt/volumeManager.h0000644000175000017500000001437514205115226016056 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2021, 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 . * * Authors: yanling * */ #ifndef VOLUMEMANAGER_H #define VOLUMEMANAGER_H #include #include #include #include #include #undef slots #undef signals #undef emit #include #include namespace Experimental_Peony{ class Volume; class Mount; class Drive; class Q_DECL_EXPORT VolumeManager : public QObject { Q_OBJECT public: static VolumeManager* getInstance(); ~VolumeManager(); /*静态的获取当前所有有效的设备分区,函数名后期再做更换*/ QList* allVaildVolumes(); void printVolumeList(); private: explicit VolumeManager(QObject *parent = nullptr); bool gpartedIsOpening(); void initManagerInfo(); //void initMonitorInfo(); //监控卷分区 QList allMounts(); QList allGMounts(); QList allVolumes(); QList allGVolumes(); QList allGDrives(); /* 获取连接的GDrive集合 */ QList allDrives(); /* 将GDrive转为Drive */ //QString guessContentType(GMount*); static void volumeAddCallback(GVolumeMonitor*,GVolume*,VolumeManager*); static void volumeRemoveCallback(GVolumeMonitor*,GVolume*,VolumeManager*); static void mountAddCallback(GVolumeMonitor*,GMount*,VolumeManager*); static void mountRemoveCallback(GVolumeMonitor*,GMount*,VolumeManager*); static void driveConnectCallback(GVolumeMonitor*,GDrive*,VolumeManager*); static void driveDisconnectCallback(GVolumeMonitor*,GDrive*,VolumeManager*); static void volumeChangeCallback(GVolumeMonitor*,GVolume*,VolumeManager*); static void mountChangedCallback(GMount *mount, VolumeManager *pThis); private: GVolumeMonitor* m_volumeMonitor = nullptr; quint64 m_volumeAddHandle; quint64 m_volumeRemoveHandle; quint64 m_volumeChangeHandle; quint64 m_mountAddHandle; quint64 m_mountRemoveHandle; quint64 m_driveConnectHandle; quint64 m_driveDisconnectHandle; bool m_gpartedIsOpening; QHash* m_volumeList = nullptr; //我应该在检测到信号时更新卷设备列表?还是在用到时重新全部get一次?感觉前者好点? Q_SIGNALS: void volumeUpdate(const Volume&,QString); //gparted打开与关闭时可能会伴随设备信息的更新 void volumeAdd(const Volume&); //volumeRemove()对应的槽内需要匹配device或者mountpoint void volumeRemove(const QString&);//1、设备被真正移除 2、gparetd打开时卸载设备 //void volumeUnmount(const QString& device); void mountAdd(const Volume&); //重设挂载点信息 void mountRemove(const QString& device); void signal_unmountFinished(const QString &uri);/* 卸载完成信号 */ void signal_mountFinished();/* 挂载完成信号,目前用于侧边栏设备挂载后路径跳转 */ }; class Q_DECL_EXPORT Drive{ public: //property QString name() const; QString icon() const; QString device() const; bool canEject() const; bool canStop() const; //bool unmountAble(); GDrive* getGDrive() const; Drive(GDrive* gdrive); ~Drive(); //method void eject(GMountUnmountFlags ejectFlag); void setMountPath(const QString& mountPath); private: GDrive* m_drive = nullptr; bool m_canEject = false ; bool m_canStop = false; QString m_name; QString m_icon; QString m_device; QString m_mountPath; private: void initDriveInfo(); }; class Q_DECL_EXPORT Mount{ public: QString name() const; QString uuid() const; QString icon() const; bool canEject() const; bool canStop() const; bool canUnmount() const; QString device() const; QString mountPoint() const; GMount* getGMount() const; Mount(GMount* gmount); ~Mount(); void unmount(); private: bool m_canEject = false ; bool m_canStop = false ; bool m_canUnmount = false ; GMount* m_mount = nullptr; QString m_name; QString m_uuid; QString m_icon; QString m_device; QString m_mountPoint; GUnixMountEntry * m_entry = nullptr; private: void initMountInfo(); void queryDeviceByMountpoint(); }; class Q_DECL_EXPORT Volume{ public: //property-to-get QString name() const; QString icon() const; QString uuid() const; QString device() const; QString mountPoint() const; GVolume* getGVolume() const; //property-to-set void setLabel(const QString& label); void setFromMount(const Mount& mount);//通过Mount求Volume void setFromDrive(const Drive& drive);//通过Drive获取Volume void setMountPoint(QString point); QString getMountPoint(); Volume* initRootVolume(); //根分区 Volume(GVolume* gvolume); Volume(const Volume& other);//深拷贝 ~Volume(); bool operator==(const Volume& other) const; bool operator==(const Volume* other) const; //bool operator==(const QString& deivce) const; //method bool canEject() const; bool canStop() const; bool canUnmount() const; bool canMount() const; void eject(GMountUnmountFlags ejectFlag); void unmount(); void mount(); void format() const; private: bool m_canEject = false ; bool m_canStop = false; bool m_canUnmount = false ; bool m_canMount = false; GVolume* m_volume = nullptr; GMount* m_gMount =nullptr; GDrive* m_gdrive = nullptr; QString m_name; QString m_uuid; QString m_icon; QString m_device; QString m_mountPoint; private: void initVolumeInfo(); }; } #endif // VOLUMEMANAGER_H peony/libpeony-qt/file-enumerator.h0000644000175000017500000002035614205101223016336 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILEENUMERATOR_H #define FILEENUMERATOR_H #include #include "peony-core_global.h" #include #include #include #include "custom-error-handler.h" class QTimer; namespace Peony { class FileInfo; class GErrorWrapper; /*! * \brief The FileEnumerator class *
* FileEnumerator is a file enumeration class with its own exception handling. * This class provides a unified interface for accessing various uri path subfiles. * It also supports pre-processing for some uri paths that require special processing * and provides interaction when needed. * The essence of this class is a wrapper of GFileEnumerator. *
*/ class PEONYCORESHARED_EXPORT FileEnumerator : public QObject { Q_OBJECT public: explicit FileEnumerator(QObject *parent = nullptr); ~FileEnumerator(); void setEnumerateDirectory(QString uri); void setEnumerateDirectory(GFile *file); void setEnumerateWithInfoJob(bool query = true); QString getEnumerateUri(); /*! * \brief prepare *
* prepare * This is an async method that do some pre-handling before * we really start an enumerating. Ofcourse, * you can enumerate a file without any preparing, but * I recommend do something preparing before we enumerate a directory. * For some special uri, such as drive in computer or remote sftp, we * can not directly enumerate their children. It is best to wait async * of prepare done, then we can enumerate the file, or get something * error messages. we should connect prepared() signal for async. *
* \see prepared(). */ void prepare(); /*! * \brief enumerateSync *
* Enumerate children of a path, blocking i/o. *
* \note It might not enum children successfully for some error. * if you want to * \see enumerateChildren(). */ void enumerateSync(); /*! * \brief getChildren * \return */ const QList> getChildren(); const QStringList getChildrenUris() { return *m_children_uris; } void setAutoDelete(bool autoDelete = true) { m_auto_delete = true; } Q_SIGNALS: /*! * \brief prepared * \param err * \param targetUri return the real uri should enumerate. * \param critical if true, show a critical dialog. *
* We often start an enumerating after prepared signal sended. * This will reduce the 'risks' of errors. *
* \see prepare(). */ void prepared(const std::shared_ptr &err = nullptr, const QString &targetUri = nullptr, bool critical = false); /*! * \brief childrenUpdated * \param uriList, uri list of newly enumerated files. *
* If we use enumerateAsync(), we might not get all * the children at once. This signal sends everytime * there are newly children found asynchronously. * connect this signal in you classes and update you data. * or connect finished signal which sends when all * children were found asynchronously. *
* \see enumerateAsync(), enumerator_next_files_async_ready_callback(); */ void childrenUpdated(const QStringList &uriList, bool isEnding = false); /*! * \brief enumerateFinished * \param successed * \retval true, if enumerate children successed. * \retval false, if enumerate children failed. * \note For other class, they might only care whether * there are children found when enumerating. * They don't care how enumerator dealing with some error. * So just tell them the last result with this signal. */ void enumerateFinished(bool successed = false); void cancelled(); public Q_SLOTS: void enumerateAsync(); /*! * \brief cancel *
* Cancel all the work of this eumerator excuting now, * including mounting, enumerating, etc. *
*/ void cancel(); protected: /*! * \brief handleError * \param err * */ void handleError(GError *err); /*! * \brief enumerateChildren, a sync method enumerate children and cached their GFile handle. * \param enumerator, handle of enum next file. */ void enumerateChildren(GFileEnumerator *enumerator); /*! * \brief enumerateTargetFile * \return target uri which original uri point to. * \note for some special uri, such as volume in 'computer:///', * or server in 'network:///', etc, the could not enumerate directly. * gvfs supplied query their target uri, if they have mounted into local. * Use this uri rather than the raw one, otherwise we might not enumerate * their children. */ GFile *enumerateTargetFile(); /*! * \brief prepare_enumerate_callback * \param file * \param res * \param p_this * \return * \see prepare(), handleError() */ static GAsyncReadyCallback prepare_enumerate_callback(GFile *file, GAsyncResult *res, FileEnumerator *p_this); /*! * \brief mount_mountable_callback * \param file * \param res * \param p_this * \return * \see handleError(). */ static GAsyncReadyCallback mount_mountable_callback(GFile *file, GAsyncResult *res, FileEnumerator *p_this); /*! * \brief mount_enclosing_volume_callback * \param file * \param res * \param p_this * \return * \see handleError(). */ static GAsyncReadyCallback mount_enclosing_volume_callback(GFile *file, GAsyncResult *res, FileEnumerator *p_this); /*! * \brief find_children_async_ready_callback * \param file * \param res * \param p_this * \return * \see enumerateAsync(). */ static GAsyncReadyCallback find_children_async_ready_callback(GFile *file, GAsyncResult *res, FileEnumerator *p_this); /*! * \brief enumerator_next_files_async_ready_callback * \param enumerator * \param res * \param p_this * \return * \see enumerateAsync(). */ static GAsyncReadyCallback enumerator_next_files_async_ready_callback(GFileEnumerator *enumerator, GAsyncResult *res, FileEnumerator *p_this); private: QString m_uri; GFile *m_root_file = nullptr; GCancellable *m_cancellable = nullptr; QList *m_children_uris = nullptr; QStringList *m_cache_uris; QTimer *m_idle; bool m_auto_delete = false; bool m_with_info_job = false; /*! * \brief m_cached_infos * \note * we could not get this cache directly. insteadly we can use * FileInfo::fromUri() to get each of them while enumerating finished. * this cache just hold the strong reference during enumerating. * once we didn't hold a ref before it deleted, it will unref automaticly. * * \see * setEnumerateWithInfoJob(), * getChildren() */ QList> m_cached_infos; /*! * \brief m_is_custom_error_handler_initialized * \ */ bool m_is_custom_error_handler_initialized = false; /*! * \brief m_custom_error_handlers * handling custom error occurred in custom vfs. * \see VFSPluginIface::customErrorHandler(). */ QMap m_custom_error_handlers; }; } #endif // FILEENUMERATOR_H peony/libpeony-qt/org.ukui.peony.settings.gschema.xml0000644000175000017500000000117014205115226021753 0ustar fengfeng true show trash dialog Displays the delete to recycle bin dialog box false show hide file show hide files in peony and peony-desktop peony/libpeony-qt/mount-operation.h0000644000175000017500000000566014205101223016401 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef MOUNTOPERATION_H #define MOUNTOPERATION_H #include "peony-core_global.h" #include #include #include namespace Peony { class ConnectServerLogin; class GErrorWrapper; /*! * \brief The MountOperation class *
* This class is a wrapper of GFileMountOperation. * MountOperation provides a interactive dialog for connecting server. * In gvfs, all kinds of remote location will use g_file_mount_enclosing_volume() * for those volumes mounting, many of them need extra infomation, such as user, password, etc. * This class will help to handle them together. *
* \note FileEnumerator::prepare() and FileEnumerator::handleError() might use this class instance. */ class PEONYCORESHARED_EXPORT MountOperation : public QObject { Q_OBJECT public: explicit MountOperation(QString uri, QObject *parent = nullptr); ~MountOperation(); void setAutoDelete(bool isAuto = true) { m_auto_delete = isAuto; } Q_SIGNALS: void finished(const std::shared_ptr &err = nullptr); void cancelled(); public Q_SLOTS: void start(); void cancel(); protected: static GAsyncReadyCallback mount_enclosing_volume_callback(GFile *volume, GAsyncResult *res, MountOperation *p_this); static void aborted_cb (GMountOperation *op, MountOperation *p_this); static void ask_question_cb (GMountOperation *op, char *message, char **choices, MountOperation *p_this); static void ask_password_cb (GMountOperation *op, const char *message, const char *default_user, const char *default_domain, GAskPasswordFlags flags, MountOperation *p_this); private: bool m_auto_delete = false; GFile *m_volume = nullptr; GMountOperation *m_op = nullptr; GCancellable *m_cancellable = nullptr; /*! * \brief m_errs * \deprecated use GErrorWrapper. */ GList *m_errs = nullptr; ConnectServerLogin *m_dlg = nullptr; }; } #endif // MOUNTOPERATION_H peony/libpeony-qt/peony-core_global.h0000644000175000017500000000206714205101223016637 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef PEONYCORE_GLOBAL_H #define PEONYCORE_GLOBAL_H #include #undef signals #undef slots #undef emit #if defined(PEONYCORE_LIBRARY) # define PEONYCORESHARED_EXPORT Q_DECL_EXPORT #else # define PEONYCORESHARED_EXPORT Q_DECL_IMPORT #endif #endif // PEONYCORE_GLOBAL_H peony/libpeony-qt/file-info-manager.h0000644000175000017500000000544514205115226016532 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILEINFOMANAGER_H #define FILEINFOMANAGER_H #include "file-info.h" #include namespace Peony { /*! * \brief The FileInfoManager class *
* This is a class used to share FileInfo instances acrossing various members. * It is a single instance class with a hash table that cached all infos. * We generally would not operate directly on instance of this class, * because FileInfo class provides an interface for this class. * use FileInfo::fromUri(), FileInfo::fromPath() or FileInfo::fromGFile() * for getting the corresponding shared data. *
* \note The memory management is based on std smart pointer, but it is not regular. * Because hash table always hold a use count of shared data. If you want to use * shared data of this class instance, you should remenmber this point: * When releasing your info resources, you aslo need to add an additional judgment that whether * there is no other member but you and manager instance hold this shared data. * If true, you should aslo remove the element in manager's hash for really releasing resources. * Otherwise, it might cause an one-time memory leak. * \see FileInfo, FileInfoJob, FileEnumerator; FileInfo::~FileInfo(), FileInfoJob::~FileInfoJob(), * FileEnumerator::~FileEnumerator(). * \bug * Even though I try my best to make share the file info data, it seems that the info is not be shared * sometimes. Maybe there were some wrong in other classes? */ class PEONYCORESHARED_EXPORT FileInfoManager { friend class FileInfo; public: static FileInfoManager *getInstance(); std::shared_ptr findFileInfoByUri(const QString &uri); //{return global_info_list->value(uri);} void lock() { m_mutex.lock(); } void unlock() { m_mutex.unlock(); } void showState(); protected: std::shared_ptr insertFileInfo(std::shared_ptr info); //{global_info_list->insert(info->uri(), info);} private: FileInfoManager(); ~FileInfoManager(); QMutex m_mutex; }; } #endif // FILEINFOMANAGER_H peony/libpeony-qt/file-copy.h0000644000175000017500000000507114205101223015124 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Ding Jing * */ #ifndef FILECOPY_H #define FILECOPY_H #include #include #include namespace Peony { /** * @brief Copy the file to the specified folder. * @note The source file must be a file, not a folder, */ class FileInfo; class FileCopy : public QObject { Q_OBJECT public: enum Status { INVALID, PAUSE, RESTART, CANCEL, RUNNING, RESUME, FINISHED, ERROR }; Q_ENUM(Status) explicit FileCopy (QString srcUri, QString destUri, GFileCopyFlags flags, GCancellable* cancel, GFileProgressCallback cb, gpointer pcd, GError** error, QObject* obj = nullptr); ~FileCopy(); /** * @brief 整个复制流程在这里 */ void run(); public Q_SLOTS: /** * @brief pause */ void pause(); /** * @brief resume */ void resume(); /** * @brief cancel */ void cancel(); private Q_SLOTS: private: void updateProgress () const; void detailError (GError** error); void sync(const GFile* destFile); private: QMutex mPause; QString mSrcUri = nullptr; QString mDestUri = nullptr; GFileProgressCallback mProgress; GFileCopyFlags mCopyFlags; GCancellable* mCancel = nullptr; // temp param GError** mError = nullptr; // temp param gpointer mProgressData; goffset mOffset = 0; // 记录当前进度 goffset mTotalSize = 0; // 记录当前进度 enum Status mStatus = INVALID; // 记录运行状态 }; }; #endif // FILECOPY_H peony/libpeony-qt/file-meta-info.cpp0000644000175000017500000001344214205101223016365 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-meta-info.h" #include "file-info-manager.h" #include using namespace Peony; std::shared_ptr FileMetaInfo::fromGFileInfo(const QString &uri, GFileInfo *g_info) { return std::make_shared(uri, g_info); } std::shared_ptr FileMetaInfo::fromUri(const QString &uri) { auto mgr = FileInfoManager::getInstance(); auto info = mgr->findFileInfoByUri(uri); if (info) return mgr->findFileInfoByUri(uri)->m_meta_info; return nullptr; } FileMetaInfo::FileMetaInfo(const QString &uri, GFileInfo *g_info) { m_uri = uri; if (g_info) { char **metainfo_attributes = g_file_info_list_attributes(g_info, "metadata"); if (metainfo_attributes) { for (int i = 0; metainfo_attributes[i] != nullptr; i++) { char *string = g_file_info_get_attribute_as_string(g_info, metainfo_attributes[i]); if (string) { auto var = QVariant(string); this->setMetaInfoVariant(metainfo_attributes[i], var, false); //qDebug()<<"======"<message; g_error_free(err); } } // g_object_unref(info); g_object_unref(file); // m_mutex.unlock(); } const QVariant FileMetaInfo::getMetaInfoVariant(const QString &key) { QString realKey = key; if (!key.startsWith("metadata::")) realKey = "metadata::" + key; if (m_meta_hash.value(realKey).isValid()) return m_meta_hash.value(realKey); //FIXME: should i use gio query meta here? return QVariant(); } const QString FileMetaInfo::getMetaInfoString(const QString &key) { return getMetaInfoVariant(key).toString(); } const QStringList FileMetaInfo::getMetaInfoStringList(const QString &key) { return getMetaInfoVariant(key).toString().split('\n'); } int FileMetaInfo::getMetaInfoInt(const QString &key) { return getMetaInfoVariant(key).toString().toInt(); } void FileMetaInfo::removeMetaInfo(const QString &key) { // if (!m_mutex.tryLock(300)) { // return; // } QString realKey = key; if (!key.startsWith("metadata::")) realKey = "metadata::" + key; m_meta_hash.remove(realKey); GFile *file = g_file_new_for_uri(m_uri.toUtf8().constData()); g_file_set_attribute(file, realKey.toUtf8().constData(), G_FILE_ATTRIBUTE_TYPE_INVALID, nullptr, G_FILE_QUERY_INFO_NONE, nullptr, nullptr); // GFileInfo *info = g_file_info_new(); // std::string tmp = realKey.toStdString(); // //g_file_info_set_attribute(info, realKey.toUtf8(), G_FILE_ATTRIBUTE_TYPE_INVALID, nullptr); // g_file_info_remove_attribute(info, realKey.toUtf8()); // //FIXME: should i add callback? // g_file_set_attributes_async(file, // info, // G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, // 0, // nullptr, // nullptr, // nullptr); // g_object_unref(info); g_object_unref(file); // m_mutex.unlock(); } peony/libpeony-qt/file-watcher.h0000644000175000017500000001203614205101223015606 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILEWATCHER_H #define FILEWATCHER_H #include #include "peony-core_global.h" #include namespace Peony { /*! * \brief The FileWatcher class *
* FileWatcher class is a wrapper of a set of GFileMonitor handles. * The most obvious difference between it and the ordinary GFileMonitor is that * it can dynamically track the monitoring directory. For example, * if your directory move to another path, the watcher will aslo change * its monitors. If you delete the path (or trash), it will be deleted * automaticly later. *
* \bug * FileWatcher can't monitor some special directory, such as a sftp:// server. * It will cause the model can not stay in sync with filesystem. This bug is the * gio's limitations of GFileMonitor. */ class PEONYCORESHARED_EXPORT FileWatcher : public QObject { Q_OBJECT public: explicit FileWatcher(QString uri = nullptr, QObject *parent = nullptr, bool isWatchMovesFlag=false); ~FileWatcher(); /*! * \brief setMonitorChildrenChange * \param monitor_children_change * \details * For most case, we do not care wether a file internal changed event * in file manager (for example, the file's content changed). * Some special files, like files in computer:///, could use this signal * to monitor the changed event that not created or deleted * (for volume file handle in computer:///, it might be mount/unmount). */ void setMonitorChildrenChange(bool monitor_children_change = true) { m_montor_children_change = monitor_children_change; } void startMonitor(); void stopMonitor(); void forceChangeMonitorDirectory(const QString &uri); const QString currentUri() {return m_uri;} /*! * \brief supportMonitor * \return * \details * Some file or directory doesn't support g_file_monitor* methods. * We can use this to ensure that if it was truely in monitoring. * If not, we might take over the handle of file change in our own * code. */ bool supportMonitor() { return m_support_monitor; } void creatorMonitor(); Q_SIGNALS: void locationChanged(const QString &oldUri, const QString &newUri); void directoryDeleted(const QString &uri); void directoryUnmounted(const QString &uri); void fileCreated(const QString &uri); void fileDeleted(const QString &uri); void fileChanged(const QString &uri); void fileRenamed(const QString &oldUri, const QString &newUri); /*! * \brief requestUpdateDirectory * \note * only used in directory not support monitor */ void requestUpdateDirectory(); void thumbnailUpdated(const QString &uri); public Q_SLOTS: void cancel(); protected: void prepare(); static void file_changed_callback(GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, FileWatcher *p_this); static void dir_changed_callback(GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, FileWatcher *p_this); void changeMonitorUri(QString uri); private: QString m_uri = nullptr; QString m_target_uri = nullptr; GFile *m_file = nullptr; GFileMonitor *m_monitor = nullptr; GFileMonitor *m_dir_monitor = nullptr; bool m_montor_children_change = false; GCancellable *m_cancellable = nullptr; /*! * \brief m_monitor_err * \deprecated use GErrorWrapper */ GError *m_monitor_err = nullptr; /*! * \brief m_dir_monitor_err * \deprecated use GErrorWrapper */ GError *m_dir_monitor_err = nullptr; gulong m_file_handle = 0; gulong m_dir_handle = 0; bool m_support_monitor = true; /* 文件和文件夹重名用先delete再create的方式(即GFileMonitorFlags使用G_FILE_MONITOR_NONE)时该标志值为false, 文件和文件夹重名用rename的方式(即GFileMonitorFlags使用G_FILE_MONITOR_WATCH_MOVES)时该标志值为true */ bool m_isWatchMovesFlag = false; }; } #endif // FILEWATCHER_H peony/libpeony-qt/libpeony-qt.qrc0000644000175000017500000000031114205115226016036 0ustar fengfeng data/libpeony-qt-light.qss data/libpeony-qt-dark.qss data/libpeony-qt-styled.qss peony/libpeony-qt/bookmark-manager.h0000644000175000017500000000416014205115226016460 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef BOOKMARKMANAGER_H #define BOOKMARKMANAGER_H #include #include #include #include "peony-core_global.h" namespace Peony { /*! * \brief The BookMarkManager class * \details * This class is use to manage bookmarks of peony. * You can add/remove custom bookmark at peony's side bar. */ class PEONYCORESHARED_EXPORT BookMarkManager : public QObject { Q_OBJECT public: static BookMarkManager *getInstance(); const QStringList getCurrentUris() { return m_uris; } bool isLoaded() { return m_is_loaded; } Q_SIGNALS: void urisLoaded(); void bookMarkAdded(const QString &uri, bool successed); void bookMarkRemoved(const QString &uri, bool successed); void bookmarkChanged(const QString oldUri, const QString newUri); public Q_SLOTS: void addBookMark(const QString &uri); void removeBookMark(const QString &uri); void renameBookmark(const QString oldUri, const QString newUri); void removeBookMark(const QStringList &uris); private: explicit BookMarkManager(QObject *parent = nullptr); ~BookMarkManager(); void addBookMarkPrivate(const QString &uri); void removeBookMarkPrivate(const QString &uri); QStringList m_uris; QSettings *m_book_mark = nullptr; bool m_is_loaded = false; QMutex m_mutex; }; } #endif // BOOKMARKMANAGER_H peony/libpeony-qt/copy-headers.sh0000755000175000017500000000020214205101223015775 0ustar fengfeng#!/bin/bash echo "copy header files" find $(dirname $0) -name "*.h" | xargs -i cp {} $(dirname $0)/development-files/header-files peony/libpeony-qt/custom-error-handler.h0000644000175000017500000000350214205101223017306 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2021, 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 . * * Authors: Yue Lan * */ #ifndef CUSTOMERRORHANDLER_H #define CUSTOMERRORHANDLER_H #include #include "peony-core_global.h" namespace Peony { class PEONYCORESHARED_EXPORT CustomErrorHandler : public QObject { Q_OBJECT public: explicit CustomErrorHandler(QObject *parent = nullptr); /*! * \brief errorCodeSupportHandling * \return a list of error code the handler supported. */ virtual QList errorCodeSupportHandling(); virtual void handleCustomError(const QString &uri, int errorCode); Q_SIGNALS: /*! * \brief finished * tell the enumerator error handling finished, and can continue * do enumeration. */ void finished(); /*! * \brief canceled * tell the enumerator error handling cancelled, enumerator will go back * to previous directory. */ void cancelled(); /*! * \brief failed * \param message * similar to cancelled(), but providing a error message for show error message. */ void failed(const QString &message); }; } #endif // CUSTOMERRORHANDLER_H peony/libpeony-qt/mount-operation.cpp0000644000175000017500000001642514205101223016735 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "mount-operation.h" //#include "connect-server-dialog.h" #include "connect-to-server-dialog.h" #include "gerror-wrapper.h" #include #include #include #include #include using namespace Peony; MountOperation::MountOperation(QString uri, QObject *parent) : QObject(parent) { m_volume = g_file_new_for_uri(uri.toUtf8().constData()); m_op = g_mount_operation_new(); m_cancellable = g_cancellable_new(); } MountOperation::~MountOperation() { disconnect(); //g_object_disconnect (G_OBJECT (m_op), "any_signal::signal_name", nullptr); g_signal_handlers_disconnect_by_func(m_op, (void *)G_CALLBACK(ask_password_cb), nullptr); g_signal_handlers_disconnect_by_func(m_op, (void *)G_CALLBACK(ask_question_cb), nullptr); g_signal_handlers_disconnect_by_func(m_op, (void *)G_CALLBACK(aborted_cb), nullptr); g_object_unref(m_volume); g_object_unref(m_op); g_object_unref(m_cancellable); //g_list_free_full(m_errs, GDestroyNotify(g_error_free)); m_dlg->deleteLater(); } void MountOperation::cancel() { g_cancellable_cancel(m_cancellable); g_object_unref(m_cancellable); m_cancellable = g_cancellable_new(); Q_EMIT cancelled(); if (m_auto_delete) deleteLater(); } void MountOperation::start() { gchar* urit = g_file_get_uri(m_volume); QUrl uri = QUrl(urit); if (uri.scheme() != "mtp") { ConnectServerLogin* dlg = new ConnectServerLogin(urit); m_dlg = dlg; //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; } } g_file_mount_enclosing_volume(m_volume, G_MOUNT_MOUNT_NONE, m_op, m_cancellable, GAsyncReadyCallback(mount_enclosing_volume_callback), this); g_signal_connect (m_op, "ask-question", G_CALLBACK(ask_question_cb), this); g_signal_connect (m_op, "ask-password", G_CALLBACK (ask_password_cb), this); g_signal_connect (m_op, "aborted", G_CALLBACK (aborted_cb), this); if (nullptr != urit) g_free(urit); } #include "global-settings.h" GAsyncReadyCallback MountOperation::mount_enclosing_volume_callback(GFile *volume, GAsyncResult *res, MountOperation *p_this) { GError *err = nullptr; g_file_mount_enclosing_volume_finish (volume, res, &err); if (err) { qDebug()<code<message<domain; auto errWarpper = GErrorWrapper::wrapFrom(err); p_this->finished(errWarpper); } else{ QUrl url = QUrl(g_file_get_uri(volume)); p_this->m_dlg->syncRemoteServer(url); p_this->finished(nullptr); } if (p_this->m_auto_delete) { p_this->disconnect(); p_this->deleteLater(); } return nullptr; } void MountOperation::ask_question_cb(GMountOperation *op, char *message, char **choices, MountOperation *p_this) { qDebug()<<"ask question cb:"<setText(message); char **choice = choices; int i = 0; while (*choice) { qDebug()<<*choice; QPushButton *button = msg_box->addButton(QString(*choice), QMessageBox::ActionRole); connect(button, &QPushButton::clicked, [=]() { g_mount_operation_set_choice(op, i); }); *choice++; i++; } //block ui msg_box->exec(); msg_box->deleteLater(); qDebug()<<"msg_box done"; g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED); } void MountOperation::ask_password_cb(GMountOperation *op, const char *message, const char *default_user, const char *default_domain, GAskPasswordFlags flags, MountOperation *p_this) { Q_UNUSED(message); Q_UNUSED(default_user); Q_UNUSED(default_domain); Q_UNUSED(flags); Q_UNUSED(p_this); //block ui if (p_this->m_dlg->anonymous()) { // try login anonymous once, if not successed, ask password. p_this->m_dlg->m_usr_btn_usr->click(); g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED); return; } else { // try get password and login once, if not successed, show message and // require input password again. if (!p_this->m_dlg->password().isEmpty()) { p_this->m_dlg->m_reg_usr_passwd_editor->setText(nullptr); g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED); return; } else { QMessageBox::information(0, 0, message); auto code = p_this->m_dlg->exec(); auto dlg = p_this->m_dlg; if (code == QDialog::Accepted) { 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()); g_mount_operation_set_anonymous(op, dlg->anonymous()); g_mount_operation_set_password_save(op, dlg->savePassword()? G_PASSWORD_SAVE_FOR_SESSION: G_PASSWORD_SAVE_NEVER); g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED); return; } if (code == QDialog::Rejected) { g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED); return; } } } g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED); } void MountOperation::aborted_cb(GMountOperation *op, MountOperation *p_this) { g_mount_operation_reply(op, G_MOUNT_OPERATION_ABORTED); p_this->disconnect(); p_this->deleteLater(); } peony/libpeony-qt/file-utils.cpp0000644000175000017500000007300614205115226015660 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-utils.h" #include "file-info.h" #include "volume-manager.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace Peony; FileUtils::FileUtils() { } QString FileUtils::getQStringFromCString(char *c_string, bool free) { QString value = c_string; if (free) g_free(c_string); return value; } QString FileUtils::getFileUri(const GFileWrapperPtr &file) { if (!G_IS_FILE (file.get()->get())) { return nullptr; } char *uri = g_file_get_uri(file.get()->get()); QString urlString = QString(uri); QUrl url = urlString; g_free(uri); char *path = g_file_get_path(file.get()->get()); if (path && !url.isLocalFile()) { QString urlString = QString("file://%1").arg(path); g_free(path); return urlString; } return urlString; } QString FileUtils::getFileBaseName(const GFileWrapperPtr &file) { char *basename = g_file_get_basename(file.get()->get()); return getQStringFromCString(basename); } QString FileUtils::getUriBaseName(const QString &uri) { QUrl url = uri; return url.fileName(); } GFileWrapperPtr FileUtils::getFileParent(const GFileWrapperPtr &file) { return wrapGFile(g_file_get_parent(file.get()->get())); } QString FileUtils::getRelativePath(const GFileWrapperPtr &dir, const GFileWrapperPtr &file) { char *relative_path = g_file_get_relative_path(dir.get()->get(), file.get()->get()); return getQStringFromCString(relative_path); } GFileWrapperPtr FileUtils::resolveRelativePath(const GFileWrapperPtr &dir, const QString &relativePath) { return wrapGFile(g_file_resolve_relative_path(dir.get()->get(), relativePath.toUtf8().constData())); } QString FileUtils::urlEncode(const QString& url) { QString decodeUrl = urlDecode(url); if (!decodeUrl.isEmpty()) { g_autofree gchar* encodeUrl = g_uri_escape_string (decodeUrl.toUtf8().constData(), ":/", true); // qDebug() << "encode url from:'" << url <<"' to '" << encodeUrl << "'"; return encodeUrl; } g_autofree gchar* encodeUrl = g_uri_escape_string (url.toUtf8().constData(), ":/", true); // qDebug() << "encode url from:'" << url <<"' to '" << encodeUrl << "'"; return encodeUrl; } QString FileUtils::urlDecode(const QString &url) { g_autofree gchar* decodeUrl = g_uri_unescape_string(url.toUtf8(), ":/"); if (!decodeUrl) { // qDebug() << "decode url from:'" << url <<"' to '" << url << "'"; return url; } // qDebug() << "decode url from:'" << url <<"' to '" << decodeUrl << "'"; return decodeUrl; } QString FileUtils::handleDuplicateName(const QString& uri) { QString handledName = nullptr; QString name = QUrl(uri).toDisplayString().split("/").last(); QRegExp regExpNum("\\(\\d+\\)"); QRegExp regExp (QString("\\ -\\ %1\\(\\d+\\)(\\.[0-9a-zA-Z\\.]+|)$").arg(QObject::tr("duplicate"))); QString dupReg = nullptr; 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 { if (name.contains(".")) { auto list = name.split("."); if (list.count() <= 1) { handledName = name + QString(" - %1(1)").arg(QObject::tr("duplicate")); } else { int pos = list.count() - 1; if (list.last() == "gz" || list.last() == "xz" || list.last() == "Z" || list.last() == "sit" || list.last() == "bz" || list.last() == "bz2") { --pos; } if (pos < 0) { pos = 0; } auto tmp = list; QStringList suffixList; for (int i = 0; i < list.count() - pos; i++) { suffixList.prepend(tmp.takeLast()); } auto suffix = suffixList.join("."); auto basename = tmp.join("."); name = basename + QString(" - %1(1)").arg(QObject::tr("duplicate")) + "." + suffix; if (name.endsWith(".")) { name.chop(1); } handledName = name; } } 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 return displayName; QString name = QUrl(uri).toDisplayString().split("/").last(); QRegExp regExpNum("\\(\\d+\\)"); auto showName = displayName; if (!name.contains(QObject::tr("duplicate"))) return displayName; QStringList matchList; int pos=0; while((pos=regExpNum.indexIn(name,pos))!=-1) { pos+=regExpNum.matchedLength(); QString result=regExpNum.cap(0); matchList<get(), G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr); return type == G_FILE_TYPE_DIRECTORY || type == G_FILE_TYPE_MOUNTABLE; } bool FileUtils::getFileIsFolder(const GFileWrapperPtr &file) { GFileType type = g_file_query_file_type(file.get()->get(), G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr); return type == G_FILE_TYPE_DIRECTORY; } bool FileUtils::getFileIsFolder(const QString &uri) { auto info = FileInfo::fromUri(uri); return info.get()->isDir(); } bool FileUtils::getFileIsSymbolicLink(const QString &uri) { auto info = FileInfo::fromUri(uri); return info.get()->isSymbolLink(); } QStringList FileUtils::getChildrenUris(const QString &directoryUri) { QStringList uris; GError *err = nullptr; GFile *top = g_file_new_for_uri(directoryUri.toUtf8().constData()); GFileEnumerator *e = g_file_enumerate_children(top, G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, &err); if (err) { g_error_free(err); } g_object_unref(top); if (!e) return uris; auto child_info = g_file_enumerator_next_file(e, nullptr, nullptr); while (child_info) { auto child = g_file_enumerator_get_child(e, child_info); auto uri = g_file_get_uri(child); QString urlString = FileUtils::urlEncode(uri); // BUG: 65889 // auto path = g_file_get_path(child); // QUrl url = urlString; // if (path && !url.isLocalFile()) { // urlString = QString("file://%1").arg(path); // g_free(path); // } else { // urlString = uri; // } uris<= QT_VERSION_CHECK(5, 10, 0) QString suffix = suffixedBaseName.chopped(suffixedBaseName.size() - index); if (suffix == ".gz" || suffix == ".xz" || suffix == ".bz" || suffix == ".bz2" || suffix == ".Z" || suffix == ".sit") { int secondIndex = suffixedBaseName.lastIndexOf('.'); suffixedBaseName.chop(suffixedBaseName.size() - secondIndex); } #else suffixedBaseName.chop(suffixedBaseName.size() - index); #endif } return suffixedBaseName; } } QString FileUtils::getFileDisplayName(const QString &uri) { auto fileInfo = FileInfo::fromUri(uri); if (uri == "file:///data") return QObject::tr("data"); return fileInfo.get()->displayName(); } QString FileUtils::getFileIconName(const QString &uri, bool checkValid) { if (checkValid) { //FIXME: replace BLOCKING api in ui thread. auto fileInfo = FileInfo::fromUri(uri); return fileInfo.get()->iconName(); if (!fileInfo.get()->isEmptyInfo()) { return fileInfo.get()->iconName(); } } auto file = wrapGFile(g_file_new_for_uri(uri.toUtf8().constData())); auto info = wrapGFileInfo(g_file_query_info(file.get()->get(), G_FILE_ATTRIBUTE_STANDARD_ICON, G_FILE_QUERY_INFO_NONE, nullptr, nullptr)); if (!G_IS_FILE_INFO (info.get()->get())) return nullptr; GIcon *g_icon = g_file_info_get_icon (info.get()->get()); QString icon_name; //do not unref the GIcon from info. if (G_IS_ICON(g_icon)) { const gchar* const* icon_names = g_themed_icon_get_names(G_THEMED_ICON (g_icon)); if (icon_names) { auto p = icon_names; if (*p) icon_name = QString (*p); if (checkValid) { while (*p) { QIcon icon = QIcon::fromTheme(*p); if (!icon.isNull()) { icon_name = QString (*p); break; } else { p++; } } } }else { //if it's a bootable-media,maybe we can get the icon from the mount directory. char *bootableIcon = g_icon_to_string(g_icon); if(bootableIcon){ icon_name = QString(bootableIcon); g_free(bootableIcon); } } } return icon_name; } GErrorWrapperPtr FileUtils::getEnumerateError(const QString &uri) { auto file = wrapGFile(g_file_new_for_uri(uri.toUtf8().constData())); GError *err = nullptr; auto enumerator = wrapGFileEnumerator(g_file_enumerate_children(file.get()->get(), G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, &err)); if (err) { return GErrorWrapper::wrapFrom(err); } return nullptr; } QString FileUtils::getTargetUri(const QString &uri) { auto fileInfo = FileInfo::fromUri(uri); return fileInfo.get()->targetUri(); } QString FileUtils::getEncodedUri(const QString &uri) { GFile *file = g_file_new_for_uri(uri.toUtf8().constData()); QString encodedUri = g_file_get_uri(file); g_object_unref(file); return encodedUri; } QString FileUtils::getSymbolicTarget(const QString &uri) { auto fileInfo = FileInfo::fromUri(uri); return fileInfo.get()->symlinkTarget(); } bool FileUtils::isMountPoint(const QString &uri) { bool flag = false; // The uri is a mount point GFile* file = g_file_new_for_uri(uri.toUtf8().constData()); GList* mounts = nullptr; GVolumeMonitor* vm = g_volume_monitor_get(); if (nullptr != vm) { mounts = g_volume_monitor_get_mounts(vm); if (nullptr != mounts) { for (GList* l = mounts; nullptr != l; l = l->next) { GMount* m = (GMount*)l->data; GFile* f = g_mount_get_root(m); if (g_file_equal(file, f)) { flag = true; g_object_unref(f); break; } g_object_unref(f); } } } if (nullptr != vm) { g_object_unref(vm); } if (nullptr != mounts) { g_list_free_full(mounts, g_object_unref); } if (nullptr != file) { g_object_unref(file); } return flag; } bool FileUtils::stringStartWithChinese(const QString &string) { if (string.isEmpty()) return false; auto firstStrUnicode = string.at(0).unicode(); return (firstStrUnicode <=0x9FA5 && firstStrUnicode >= 0x4E00); } bool FileUtils::stringLesserThan(const QString &left, const QString &right) { bool leftStartWithChinese = stringStartWithChinese(left); bool rightStartWithChinese = stringStartWithChinese(right); if (!(!leftStartWithChinese && !rightStartWithChinese)) { return leftStartWithChinese; } return left.toLower() < right.toLower(); } const QString FileUtils::getParentUri(const QString &uri) { auto file = wrapGFile(g_file_new_for_uri(uri.toUtf8().constData())); auto parent = getFileParent(file); auto parentUri = getFileUri(parent); return parentUri == uri? nullptr: parentUri; } const QString FileUtils::getOriginalUri(const QString &uri) { auto file = wrapGFile(g_file_new_for_uri(uri.toUtf8().constData())); auto originalUri = getFileUri(file); return originalUri; } bool FileUtils::isStandardPath(const QString &uri) { QUrl url = uri; QDir templateDir(g_get_user_special_dir(G_USER_DIRECTORY_TEMPLATES)); QString desktopPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); QString documentPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); QString picturePath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); QString videoPath= QStandardPaths::writableLocation(QStandardPaths::MoviesLocation); QString downloadPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); QString musicPath = QStandardPaths::writableLocation(QStandardPaths::MusicLocation); QStringList mStandardPaths; //qDebug() << "isStandardPath :" < 1) { auto last = list.last(); if (last.startsWith(" ")) last.remove(0, 1); volumeName = last; } else { volumeName = displayName; } handleVolumeLabelForFat32(volumeName,unixDeviceName); return true; } bool FileUtils::isReadonly(const QString& uri) { GFile *file = g_file_new_for_uri(uri.toUtf8().constData()); GFileInfo *info = g_file_query_info(file, "access::*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); g_object_unref(file); if (info) { bool read = g_file_info_get_attribute_boolean(info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ); bool write = g_file_info_get_attribute_boolean(info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE); bool execute = g_file_info_get_attribute_boolean(info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE); if (read && !write && !execute) { return true; } } return false; } bool FileUtils::isFileDirectory(const QString &uri) { bool isFolder = false; GFile *file = g_file_new_for_uri(uri.toUtf8().constData()); isFolder = g_file_query_file_type(file, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr) == G_FILE_TYPE_DIRECTORY; g_object_unref(file); return isFolder; } bool FileUtils::isFileUnmountable(const QString &uri) { GFile *file = g_file_new_for_uri(uri.toUtf8().constData()); GFileInfo *info = g_file_query_info(file, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); g_object_unref(file); if (info) { bool unmountable = g_file_info_get_attribute_boolean(info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT); g_object_unref(info); return unmountable; } return false; } /* @func: convert a ascii string to unicode string. 将一个ascii字符串转换为unicode字符串 * @gbkName a string that needs to be converted from ascii to Unicode. eg:"\\xb8\\xfc\\xd0\\xc2CODE" */ QString transcodeForGbkCode(QByteArray gbkName, QString &volumeName) { int i; QByteArray dest,tmp; QString name; int len = gbkName.size(); for(i = 0x0; i < len; ++i){ if(92 == gbkName.at(i)){ if(4 == tmp.size()) dest.append(QByteArray::fromHex(tmp)); else{ if(tmp.size() > 4){ dest.append(QByteArray::fromHex(tmp.left(4))); dest.append(tmp.mid(4)); }else dest.append(tmp); } tmp.clear(); tmp.append(gbkName.at(i)); continue; }else if(tmp.size() > 0){ tmp.append(gbkName.at(i)); continue; }else dest.append(gbkName.at(i)); } if(4 == tmp.size()) dest.append(QByteArray::fromHex(tmp)); else{ if(tmp.size() > 4){ dest.append(QByteArray::fromHex(tmp.left(4))); dest.append(tmp.mid(4)); }else dest.append(tmp); } /* * gio的api获取的卷名和/dev/disk/by-label先的名字不一致,有可能是卷名 * 中含有特殊字符,导致/dev/disk/label下的卷名含有转义字符,导致二者的名字不一致 * 而不是编码格式的不一致导致的,比如卷名:“数据光盘(2020-08-22)”,在/dev/disk/by-label * 写的名字:"数据光盘\x282020-08-22\x29",经过上述处理之后可以去除转义字符,在判断一次 * 是否相等。比较完美的解决方案是找到能够判断字符串的编码格式,目前还没有找到实现方式,需要进一步完善 */ name = QString(dest); if (name == volumeName){ return name; } name = QTextCodec::codecForName("GBK")->toUnicode(dest); //name = QTextCodec::codecForLocale()->toUnicode(dest); return name; } /* @func: determines whether the @volumeName needs to be transcoded. 判断字符串是否需要转码. * @volumeName a string that needs to be converted from ascii to Unicode. eg:"\\xb8\\xfc\\xd0\\xc2CODE" * @unixDeviceName a device name. eg: /dev/sdb */ void FileUtils::handleVolumeLabelForFat32(QString &volumeName,const QString &unixDeviceName){ if (unixDeviceName.isEmpty()) return; GVolumeMonitor *vm = g_volume_monitor_get(); GList *volumes = g_volume_monitor_get_volumes(vm); GList *l = volumes; while (l) { GVolume *volume = static_cast(l->data); g_autofree char *volume_unix_dev = g_volume_get_identifier(volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); if (unixDeviceName == volume_unix_dev) { g_autofree char *volume_name = g_volume_get_name(volume); volumeName = volume_name; break; } l = l->next; } g_list_free_full(volumes, g_object_unref); g_object_unref(vm); return; QFileInfoList diskList; QFileInfo diskLabel; QDir diskDir; QString partitionName,linkTarget; QString tmpName,finalName; int i; QRegExp rx("[^\u4e00-\u9fa5]"); //non-chinese characters diskDir.setPath("/dev/disk/by-label"); if(!diskDir.exists()) //this means: volume has no name. return; // or there no mobile devices. diskList = diskDir.entryInfoList(); //all file from dir. /* eg: unixDeviceName == "/dev/sdb4" * partitionName == "sdb4" */ partitionName = unixDeviceName.mid(unixDeviceName.lastIndexOf('/')+1); for(i = 0; i < diskList.size(); ++i){ diskLabel = diskList.at(i); linkTarget = diskLabel.symLinkTarget(); linkTarget = linkTarget.mid(linkTarget.lastIndexOf('/')+1); if(linkTarget == partitionName) break; linkTarget.clear(); } if(!linkTarget.isEmpty()) tmpName = diskLabel.fileName();//可能带有乱码的名字 if(!tmpName.isEmpty()){ if(tmpName == volumeName || !tmpName.contains(rx)){ //ntfs、exfat格式或者非纯中文名的fat32设备,这个设备的名字不需要转码 volumeName = tmpName; return; } else { finalName = transcodeForGbkCode(tmpName.toLocal8Bit(), volumeName); if(!finalName.isEmpty()) volumeName = finalName; } } } /* @Func: return abstract device path * @uri : such as "computer:///xxx.drive" * @return: nullptr or such as "/dev/sdb1" */ QString FileUtils::getUnixDevice(const QString &uri) { GFile* file; GFileInfo* fileInfo; GCancellable* cancel; QString devicePath,targetUri; const char *tmpPath; char *mountPoint; if(uri.isEmpty()) return nullptr; cancel = g_cancellable_new(); file = g_file_new_for_uri(uri.toUtf8().data()); if(!file ||!cancel) return nullptr; //query device path by "mountable::unix-device-file" fileInfo = g_file_query_info(file,"*",G_FILE_QUERY_INFO_NONE,cancel,NULL); tmpPath = g_file_info_get_attribute_as_string(fileInfo,G_FILE_ATTRIBUTE_MOUNTABLE_UNIX_DEVICE_FILE); devicePath = tmpPath; if(!devicePath.isEmpty()){ g_object_unref(fileInfo); g_cancellable_cancel(cancel); g_object_unref(cancel); g_object_unref(file); return devicePath; } //query device path by "standard::target-uri" targetUri = getTargetUri(uri); if(targetUri.isEmpty()) return nullptr; mountPoint = g_filename_from_uri(targetUri.toUtf8().data(),NULL,NULL); if(mountPoint) tmpPath = Peony::VolumeManager::getUnixDeviceFileFromMountPoint(mountPoint); devicePath = tmpPath; g_free(mountPoint); g_object_unref(fileInfo); g_cancellable_cancel(cancel); g_object_unref(cancel); g_object_unref(file); return devicePath; } double FileUtils::getDeviceSize(const gchar * device_name) { struct stat statbuf; const gchar *crypto_backing_device; UDisksObject *object, *crypto_backing_object; UDisksBlock *block; UDisksClient *client =udisks_client_new_sync (NULL,NULL); object = NULL; if (stat (device_name, &statbuf) != 0) { return -1; } block = udisks_client_get_block_for_dev (client, statbuf.st_rdev); if (block == NULL) { return -1; } object = UDISKS_OBJECT (g_dbus_interface_dup_object (G_DBUS_INTERFACE (block))); g_object_unref (block); crypto_backing_device = udisks_block_get_crypto_backing_device ((udisks_object_peek_block (object))); crypto_backing_object = udisks_client_get_object (client, crypto_backing_device); if (crypto_backing_object != NULL) { g_object_unref (object); object = crypto_backing_object; } block = udisks_object_get_block (object); guint64 size = udisks_block_get_size(block); double volume_size =(double)size/1024/1024/1024; g_clear_object(&client); g_object_unref(object); g_object_unref(block); return volume_size; } quint64 FileUtils::getFileSystemSize(QString uri) { QString unixDevice,dbusPath; quint64 total = 0; unixDevice = FileUtils::getUnixDevice(uri); if (unixDevice.isEmpty()) { return total; } dbusPath = "/org/freedesktop/UDisks2/block_devices/" + unixDevice.split("/").last(); if (! QDBusConnection::systemBus().isConnected()) return total; QDBusInterface blockInterface("org.freedesktop.UDisks2", dbusPath, "org.freedesktop.UDisks2.Filesystem", QDBusConnection::systemBus()); if(blockInterface.isValid()) total = blockInterface.property("Size").toULongLong(); return total; } QString FileUtils::getFileSystemType(QString uri) { QString unixDevice,dbusPath; QString fsType = ""; unixDevice = getUnixDevice(uri); if (unixDevice.isEmpty()) { return fsType; } dbusPath = "/org/freedesktop/UDisks2/block_devices/" + unixDevice.split("/").last(); if (! QDBusConnection::systemBus().isConnected()) return fsType; QDBusInterface blockInterface("org.freedesktop.UDisks2", dbusPath, "org.freedesktop.UDisks2.Block", QDBusConnection::systemBus()); if(blockInterface.isValid()) fsType = blockInterface.property("IdType").toString(); //if need diff FAT16 and FAT32, should use IdVersion // if(fsType == "" && blockInterface.isValid()) // fsType = blockInterface.property("IdVersion").toString(); return fsType; } peony/libpeony-qt/complementary-style.cpp0000644000175000017500000002275614205101223017616 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "complementary-style.h" #include "tool-bar.h" #include "location-bar.h" #include #include #include #include #include using namespace Peony; static ComplementaryStyle *global_instance = nullptr; ComplementaryStyle::ComplementaryStyle(QStyle *parent) : QProxyStyle(parent) { m_styled_menu = new QMenu; m_styled_button = new QPushButton; } ComplementaryStyle::~ComplementaryStyle() { m_styled_menu->deleteLater(); m_styled_button->deleteLater(); } ComplementaryStyle *ComplementaryStyle::getStyle() { if (!global_instance) global_instance = new ComplementaryStyle; return global_instance; } void ComplementaryStyle::polish(QWidget *widget) { QProxyStyle::polish(widget); if (auto button = qobject_cast(widget)) { //fix push button use as icon caused color issues if (! button->property("isIcon").toBool()) { button->setProperty("useIconHighlightEffect", true); button->setProperty("iconHighlightEffectMode", 1); button->setProperty("fillIconSymbolicColor", true); } return; } if (auto menu = qobject_cast(widget)) { menu->setProperty("useIconHighlightEffect", true); menu->setProperty("iconHighlightEffectMode", 1); menu->setProperty("fillIconSymbolicColor", true); return; } } int ComplementaryStyle::styleHint(QStyle::StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const { switch (hint) { case QStyle::SH_Menu_Scrollable: { return 1; } default: return QProxyStyle::styleHint(hint, option, widget, returnData); } } void ComplementaryStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { if (widget && widget->inherits("QMenu")) { return QProxyStyle::drawPrimitive(element, option, painter, widget); } switch (element) { case QStyle::PE_FrameFocusRect: case QStyle::PE_IndicatorToolBarSeparator: { //do not draw toolbar separator and focus rect return; } case PE_IndicatorArrowDown: { ToolBar *toolBar = qobject_cast(widget->parentWidget()); LocationBar *locationBar = qobject_cast(widget->parentWidget()); if (toolBar) { auto rect = option->rect; rect.setY((rect.top()+rect.bottom())/2 - 8); rect.setWidth(16); rect.setHeight(16); if (option->state & State_Sunken) { rect.adjust(1, 1, 1, 1); } QIcon arrowDown = QIcon::fromTheme("ukui-down-symbolic", QIcon(":/icons/ukui-down-symbolic")); arrowDown.paint(painter, rect.adjusted(-3, 0, -3, 0)); return; } else if (locationBar) { auto rect = option->rect; rect.setY((rect.top()+rect.bottom())/2 - 8); rect.setX((rect.left()+rect.right())/2 - 8); rect.setWidth(16); rect.setHeight(16); if (option->state & State_Sunken) { QIcon arrowDown = QIcon::fromTheme("ukui-down-symbolic", QIcon(":/icons/ukui-down-symbolic")); arrowDown.paint(painter, rect.adjusted(1, 1, 1, 1)); } else { QIcon arrowDown = QIcon::fromTheme("ukui-end-symbolic", QIcon(":/icons/ukui-end-symbolic")); arrowDown.paint(painter, rect.adjusted(1, 1, 1, 1)); } return; } break; } default: break; } return QProxyStyle::drawPrimitive(element, option, painter, widget); } void ComplementaryStyle::drawComplexControl(QStyle::ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p, const QWidget *widget) const { switch (cc) { case QStyle::CC_ToolButton: { if (const QStyleOptionToolButton *toolbutton = qstyleoption_cast(opt)) { //Peony::ToolBar has different style with other toolbar. ToolBar *parentWidget = qobject_cast(widget->parentWidget()); QRect button, menuarea; button = proxy()->subControlRect(cc, toolbutton, SC_ToolButton, widget); menuarea = proxy()->subControlRect(cc, toolbutton, SC_ToolButtonMenu, widget); State bflags = toolbutton->state & ~State_Sunken; if (bflags & State_AutoRaise) { if (!(bflags & State_MouseOver) || !(bflags & State_Enabled)) { bflags &= ~State_Raised; } } State mflags = bflags; if (toolbutton->state & State_Sunken) { if (toolbutton->activeSubControls & SC_ToolButton) bflags |= State_Sunken; mflags |= State_Sunken; } QStyleOption tool = *toolbutton; if (toolbutton->subControls & SC_ToolButton) { if (bflags & (State_Sunken | State_On | State_Raised)) { tool.rect = button; tool.state = bflags; if (parentWidget) { if (toolbutton->features.testFlag(QStyleOptionToolButton::Menu)) { tool.rect = QRect(button.topLeft(), QSize(button.width() + menuarea.width(), button.height())); } proxy()->drawPrimitive(PE_PanelButtonTool, &tool, p, widget); } else { proxy()->drawPrimitive(PE_PanelButtonTool, &tool, p, widget); } } else { if (parentWidget) { tool.rect = button; tool.state = bflags; if (toolbutton->features.testFlag(QStyleOptionToolButton::HasMenu)) { tool.rect = QRect(button.topLeft(), QSize(button.width() + menuarea.width(), button.height())); } qDrawShadePanel(p, tool.rect.adjusted(1, 1, -1, -1), m_styled_menu->palette().button().color(), false, 0, &m_styled_menu->palette().brush(QPalette::Normal, QPalette::Button)); } } } if (toolbutton->state & State_HasFocus) { QStyleOptionFocusRect fr; fr.QStyleOption::operator=(*toolbutton); fr.rect.adjust(3, 3, -3, -3); if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup) fr.rect.adjust(0, 0, -proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator, toolbutton, widget), 0); proxy()->drawPrimitive(PE_FrameFocusRect, &fr, p, widget); } QStyleOptionToolButton label = *toolbutton; label.state = bflags; int fw = proxy()->pixelMetric(PM_DefaultFrameWidth, opt, widget); label.rect = button.adjusted(fw, fw, -fw, -fw); label.palette.setColor(QPalette::Window, m_styled_menu->palette().window().color()); proxy()->drawControl(CE_ToolButtonLabel, &label, p, widget); if (toolbutton->subControls & SC_ToolButtonMenu) { tool.rect = menuarea;//.adjusted(1, 1, -1, -1); tool.state = mflags; tool.palette.setColor(QPalette::Button, m_styled_button->palette().highlight().color()); //FIXME: paint indicator button background correctly. if (mflags & (State_Sunken | State_On | State_Raised)) { if (parentWidget) { //do nothing } else { proxy()->drawPrimitive(PE_PanelButtonCommand, &tool, p, widget); } } drawPrimitive(PE_IndicatorArrowDown, &tool, p, widget); } else if (toolbutton->features & QStyleOptionToolButton::HasMenu) { int mbi = proxy()->pixelMetric(PM_MenuButtonIndicator, toolbutton, widget); QRect ir = toolbutton->rect; QStyleOptionToolButton newBtn = *toolbutton; newBtn.rect = QRect(ir.right() + 5 - mbi, ir.y() + ir.height() - mbi + 4, mbi - 6, mbi - 6); newBtn.rect = visualRect(toolbutton->direction, button, newBtn.rect); drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget); } } break; } default: return QProxyStyle::drawComplexControl(cc, opt, p, widget); } } peony/libpeony-qt/file-watcher.cpp0000644000175000017500000002410514205115226016151 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-watcher.h" #include "gerror-wrapper.h" #include "file-label-model.h" #include #include "file-utils.h" #include "file-operation-manager.h" #include "file-info.h" #include "volume-manager.h" #include "volumeManager.h" #include using namespace Peony; FileWatcher::FileWatcher(QString uri, QObject *parent, bool isWatchMovesFlag) : QObject(parent), m_isWatchMovesFlag(isWatchMovesFlag) { if (uri.startsWith("thumbnail://")) return; m_uri = uri; m_target_uri = uri; m_file = g_file_new_for_uri(uri.toUtf8().constData()); m_cancellable = g_cancellable_new(); //monitor target file if existed. prepare(); creatorMonitor(); FileOperationManager::getInstance()->registerFileWatcher(this); } FileWatcher::~FileWatcher() { FileOperationManager::getInstance()->unregisterFileWatcher(this); disconnect(); //qDebug()<<"~FileWatcher"< * If file handle has target uri, we need monitor file that target uri point to. * FileWatcher::prepare() just query if there is a target uri of current file handle, * and it asumed that everything is no problem. If you want to use * a file watcher instance, I recommend you call a file enumerator class instance * with FileEnumerator::prepare() and wait it finished first. *
* \see FileEnumerator::prepare(). */ void FileWatcher::prepare() { auto fileInfo = FileInfo::fromGFile(m_file); auto targetUri = fileInfo.get()->targetUri(); if (!targetUri.isNull()) { g_object_unref(m_file); m_file = g_file_new_for_uri(targetUri.toUtf8().constData()); m_target_uri = targetUri; } return; } void FileWatcher::cancel() { if (!m_cancellable) return; g_cancellable_cancel(m_cancellable); g_object_unref(m_cancellable); m_cancellable = g_cancellable_new(); } void FileWatcher::startMonitor() { //make sure only connect once in a watcher. stopMonitor(); m_file_handle = g_signal_connect(m_monitor, "changed", G_CALLBACK(file_changed_callback), this); m_dir_handle = g_signal_connect(m_dir_monitor, "changed", G_CALLBACK(dir_changed_callback), this); connect(FileLabelModel::getGlobalModel(), &FileLabelModel::fileLabelChanged, this, [=](const QString &uri) { auto parentUri = FileUtils::getParentUri(uri); QUrl parentUrl = parentUri; if (parentUri == m_uri || parentUri == m_target_uri || parentUrl.toDisplayString() == m_uri || parentUrl.toDisplayString() == m_target_uri) { Q_EMIT fileChanged(uri); qDebug()<<"file label changed"< 0) { g_signal_handler_disconnect(m_monitor, m_file_handle); m_file_handle = 0; } if (m_dir_handle > 0) { g_signal_handler_disconnect(m_dir_monitor, m_dir_handle); m_dir_handle = 0; } } void FileWatcher::forceChangeMonitorDirectory(const QString &uri) { changeMonitorUri(uri); } void FileWatcher::creatorMonitor() { GError *err1 = nullptr; m_monitor = g_file_monitor_file(m_file, G_FILE_MONITOR_WATCH_MOVES, m_cancellable, &err1); if (err1) { qDebug()<code<message; g_error_free(err1); m_support_monitor = false; } GError *err2 = nullptr; GFileMonitorFlags flag = m_isWatchMovesFlag?G_FILE_MONITOR_WATCH_MOVES:G_FILE_MONITOR_NONE; m_dir_monitor = g_file_monitor_directory(m_file, flag, m_cancellable, &err2); if (err2) { qDebug()<code<message; g_error_free(err2); m_support_monitor = false; } } void FileWatcher::changeMonitorUri(QString uri) { QString oldUri = m_uri; stopMonitor(); cancel(); m_uri = uri; m_target_uri = uri; if (m_file) g_object_unref(m_file); if (m_monitor) g_object_unref(m_monitor); if (m_dir_monitor) g_object_unref(m_dir_monitor); m_file = g_file_new_for_uri(uri.toUtf8().constData()); prepare(); creatorMonitor(); startMonitor(); Q_EMIT locationChanged(oldUri, m_uri); } void FileWatcher::file_changed_callback(GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, FileWatcher *p_this) { //qDebug()<<"file_changed_callback"<changeMonitorUri(uri); break; } case G_FILE_MONITOR_EVENT_DELETED: { auto uri = g_file_get_uri(file); bool shouldSendSignal = p_this->m_target_uri == uri; if (uri) g_free(uri); if (!shouldSendSignal) return; p_this->stopMonitor(); p_this->cancel(); //qDebug()<m_target_uri; Q_EMIT p_this->directoryDeleted(p_this->m_target_uri); break; } case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: { char *uri = g_file_get_uri(file); qDebug()<fileChanged(uri); g_free(uri); break; } default: break; } } void FileWatcher::dir_changed_callback(GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, FileWatcher *p_this) { //qDebug()<<"dir_changed_callback"; Q_UNUSED(monitor); Q_UNUSED(other_file); switch (event_type) { case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: { if (p_this->m_montor_children_change) { char *uri = g_file_get_uri(file); QString changedFileUri = uri; //qDebug()<fileChanged(changedFileUri); } break; } case G_FILE_MONITOR_EVENT_CREATED: case G_FILE_MONITOR_EVENT_MOVED_IN: { char *uri = g_file_get_uri(file); QString createdFileUri = uri; //qDebug()<<"***create uri***"<fileCreated(createdFileUri); break; } case G_FILE_MONITOR_EVENT_DELETED: case G_FILE_MONITOR_EVENT_MOVED_OUT: { char *uri = g_file_get_uri(file); QString deletedFileUri = uri; //qDebug()<<"***delete uri***"<fileDeleted(deletedFileUri); break; } case G_FILE_MONITOR_EVENT_RENAMED: { char *old_uri = g_file_get_uri (file); char *new_uri = g_file_get_uri(other_file); Q_EMIT p_this->fileRenamed(old_uri,new_uri); qDebug()<<"***oldUri***newUri***"<directoryUnmounted(deletedFileUri); break; } default: break; } } peony/libpeony-qt/file-copy.cpp0000644000175000017500000003314514205115226015472 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Ding Jing * */ #include "file-copy.h" #include "file-utils.h" #include #include #include #include #include #include "file-info.h" #include "file-info-job.h" #define BUF_SIZE 1024000 #define SYNC_INTERVAL 10 using namespace Peony; static gchar* get_fs_type (char* path); FileCopy::FileCopy (QString srcUri, QString destUri, GFileCopyFlags flags, GCancellable* cancel, GFileProgressCallback cb, gpointer pcd, GError** error, QObject* obj) : QObject (obj) { mSrcUri = FileUtils::urlEncode(srcUri); mDestUri = FileUtils::urlEncode(destUri); QString destUrit = nullptr; mCopyFlags = flags; mCancel = cancel; mProgress = cb; mProgressData = pcd; mError = error; } FileCopy::~FileCopy() { } void FileCopy::pause () { mStatus = PAUSE; mPause.tryLock(); } void FileCopy::resume () { mStatus = RESUME; mPause.unlock(); } void FileCopy::cancel() { if (mCancel) { g_cancellable_cancel (mCancel); } mPause.unlock(); } void FileCopy::detailError (GError** error) { if (nullptr == error || nullptr == *error || nullptr == mError) { return; } g_set_error(mError, (*error)->domain, (*error)->code, "%s", (*error)->message); g_error_free(*error); *error = nullptr; } void FileCopy::sync(const GFile* destFile) { g_autofree char* uri = g_file_get_uri(const_cast(destFile)); g_autofree char* path = g_file_get_path(const_cast(destFile)); // it's not possible g_return_if_fail(uri && path); // 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())) { return; } // execute sync QProcess p; p.setProgram("sync"); p.setArguments(QStringList() << "-f" << path); p.start(); p.waitForFinished(-1); } void FileCopy::updateProgress () const { if (nullptr == mProgress) { return; } mProgress(mOffset, mTotalSize, mProgressData); } void FileCopy::run () { int syncTim = 0; GError* error = nullptr; gssize readSize = 0; gssize writeSize = 0; GFileInputStream* readIO = nullptr; GFileOutputStream* writeIO = nullptr; GFile* srcFile = nullptr; GFile* destFile = nullptr; GFileInfo* srcFileInfo = nullptr; GFileInfo* destFileInfo = nullptr; GFileType srcFileType = G_FILE_TYPE_UNKNOWN; GFileType destFileType = G_FILE_TYPE_UNKNOWN; g_autofree gchar* buf = (char*)g_malloc0(sizeof (char) * BUF_SIZE); g_autoptr (GFile) destDir = NULL; srcFile = g_file_new_for_uri(FileUtils::urlEncode(mSrcUri).toUtf8()); destFile = g_file_new_for_uri(FileUtils::urlEncode(mDestUri).toUtf8()); // it's impossible if (nullptr == srcFile || nullptr == destFile) { error = g_error_new (1, G_IO_ERROR_INVALID_ARGUMENT,"%s", tr("Error in source or destination file path!").toUtf8().constData()); detailError(&error); goto out; } // impossible srcFileType = g_file_query_file_type(srcFile, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr); if (G_FILE_TYPE_DIRECTORY == srcFileType) { error = g_error_new (1, G_IO_ERROR_INVALID_ARGUMENT,"%s", tr("Error in source or destination file path!").toUtf8().constData()); detailError(&error); goto out; } destFileType = g_file_query_file_type(destFile, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr); if (G_FILE_TYPE_DIRECTORY == destFileType) { mDestUri = mDestUri + "/" + mSrcUri.split("/").last(); g_object_unref(destFile); destFile = g_file_new_for_uri(FileUtils::urlEncode(mDestUri).toUtf8()); if (nullptr == destFile) { error = g_error_new (1, G_IO_ERROR_INVALID_ARGUMENT,"%s", tr("Error in source or destination file path!").toUtf8().constData()); detailError(&error); goto out; } } // check file status if (FileUtils::isFileExsit(mDestUri)) { if (mCopyFlags & G_FILE_COPY_OVERWRITE) { g_file_delete(destFile, nullptr, &error); if (nullptr != error) { detailError(&error); goto out; } } else if (mCopyFlags & G_FILE_COPY_BACKUP) { do { QStringList newUrl = mDestUri.split("/"); newUrl.pop_back(); newUrl.append(FileUtils::handleDuplicateName(FileUtils::urlDecode(mDestUri))); mDestUri = newUrl.join("/"); } while (FileUtils::isFileExsit(mDestUri)); if (nullptr != destFile) { g_object_unref(destFile); } destFile = g_file_new_for_uri(FileUtils::urlEncode(mDestUri).toUtf8()); } else { error = g_error_new (1, G_IO_ERROR_EXISTS, "%s", QString(tr("The dest file \"%1\" has existed!")).arg(mDestUri).toUtf8().constData()); detailError(&error); goto out; } } srcFileInfo = g_file_query_info(srcFile, "standard::*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, mCancel ? mCancel : nullptr, &error); if (nullptr != error) { mTotalSize = 0; qDebug() << "srcFile: " << mSrcUri << " querry info error: " << error->message; detailError(&error); } else { mTotalSize = g_file_info_get_size(srcFileInfo); } qDebug() << "copy - src: " << mSrcUri << " to: " << mDestUri; // check dest filesystem destDir = g_file_get_parent (destFile); if (destDir) { g_autoptr (GMount) destMount = g_file_find_enclosing_mount (destDir, NULL, NULL); if (destMount) { g_autoptr (GFile) destMountFile = g_mount_get_default_location (destMount); if (destMountFile) { g_autofree gchar* mountPath = g_file_get_path (destMountFile); if (mountPath) { g_autofree gchar* fstype = get_fs_type (mountPath); if (!g_strcmp0 (fstype, "vfat") || !g_strcmp0 (fstype, "fat32")) { if (mTotalSize >= 4294967296) { error = g_error_new (1, G_IO_ERROR_NOT_SUPPORTED, "%s", QString(tr("Vfat/FAT32 file systems do not support a single file that occupies more than 4 GB space!")).toUtf8 ().constData ()); detailError(&error); goto out; } } } } } } // read io stream readIO = g_file_read(srcFile, mCancel ? mCancel : nullptr, &error); if (nullptr != error) { detailError(&error); goto out; } // write io stream writeIO = g_file_create(destFile, G_FILE_CREATE_REPLACE_DESTINATION, mCancel ? mCancel : nullptr, &error); if (nullptr != error) { // it's impossible for 'buf' is null if (!buf || G_IO_ERROR_NOT_SUPPORTED == error->code) { qWarning() << "g_file_copy " << error->code << " -- " << error->message; g_error_free(error); error = nullptr; g_file_copy(srcFile, destFile, mCopyFlags, mCancel, mProgress, mProgressData, &error); if (error) { qWarning() << "g_file_copy error:" << error->code << " -- " << error->message; detailError(&error); mStatus = ERROR; } else { mStatus = FINISHED; } } else { detailError(&error); qDebug() << "create dest file error!" << mDestUri << " == " << g_file_get_uri(destFile); } goto out; } if (!readIO || !writeIO) { error = g_error_new (1, G_IO_ERROR_FAILED,"%s", tr("Error opening source or destination file!").toUtf8().constData()); detailError(&error); goto out; } while (true) { if (RUNNING == mStatus) { if (nullptr != mCancel && g_cancellable_is_cancelled(mCancel)) { mStatus = CANCEL; continue; } memset(buf, 0, BUF_SIZE); if (++syncTim > SYNC_INTERVAL) { syncTim = 0; sync(destFile); } mPause.lock(); // read data readSize = g_input_stream_read(G_INPUT_STREAM(readIO), buf, BUF_SIZE - 1, mCancel ? mCancel : nullptr, &error); if (0 == readSize && nullptr == error) { mStatus = FINISHED; mPause.unlock(); continue; } else if (nullptr != error) { detailError(&error); mStatus = ERROR; mPause.unlock(); continue; } mPause.unlock(); // write data writeSize = g_output_stream_write(G_OUTPUT_STREAM(writeIO), buf, readSize, mCancel ? mCancel : nullptr, &error); if (nullptr != error) { qDebug() << "write destfile: " << mDestUri << " error: " << error->message; detailError(&error); mStatus = ERROR; continue; } if (readSize != writeSize) { // it's impossible qDebug() << "read file: " << mSrcUri << " --- write file: " << mDestUri << " size not inconsistent"; error = g_error_new (1, G_IO_ERROR_FAILED,"%s", tr("Please confirm that the device controls are insufficient!").toUtf8().constData()); detailError(&error); mStatus = ERROR; continue; } if (mOffset <= mTotalSize) { mOffset += writeSize; } updateProgress (); } else if (CANCEL == mStatus) { error = g_error_new(1, G_IO_ERROR_CANCELLED, "%s", tr("operation cancel").toUtf8().constData()); detailError(&error); g_file_delete (destFile, nullptr, nullptr); break; } else if (ERROR == mStatus) { g_file_delete (destFile, nullptr, nullptr); break; } else if (FINISHED == mStatus) { qDebug() << "copy file finish!"; break; } else if (PAUSE == mStatus) { if (mPause.tryLock(3000)) { if (RESUME == mStatus) { mPause.unlock(); } mStatus = RUNNING; } } else { mStatus = RUNNING; } } out: // if copy sucessed, flush all data if (FINISHED == mStatus && g_file_query_exists(destFile, nullptr)) { // copy file attribute // It is possible that some file systems do not support file attributes g_file_copy_attributes(srcFile, destFile, G_FILE_COPY_ALL_METADATA, nullptr, &error); if (nullptr != error) { qWarning() << "copy attribute error:" << error->code << " --- " << error->message; g_error_free(error); error = nullptr; } sync(destFile); } else { // some special detail for mtp if (mSrcUri.startsWith("mtp:///") || mDestUri.startsWith("mtp:///")) { if (mError) { g_error_free(*mError); *mError = nullptr; g_set_error(mError, 1, G_IO_ERROR_NOT_SUPPORTED, "%s", tr("File opening failure").toUtf8().constData()); } } } if (nullptr != readIO) { g_input_stream_close (G_INPUT_STREAM(readIO), nullptr, nullptr); g_object_unref(readIO); } if (nullptr != writeIO) { g_output_stream_close (G_OUTPUT_STREAM(writeIO), nullptr, nullptr); g_object_unref(writeIO); } if (nullptr != error) { g_error_free(error); } if (nullptr != srcFile) { g_object_unref(srcFile); } if (nullptr != destFile) { g_object_unref(destFile); } if (nullptr != srcFileInfo) { g_object_unref(srcFileInfo); } if (nullptr != destFileInfo) { g_object_unref(destFileInfo); } } /** * @note * 同步了 glib 针对vfat/fat32 单文件大于4g,提前提醒用户的补丁, * 由于 gio 获取文件系统类型不准确(据康哥反馈会把某些设备vfat/fat32、ntfs文件系统都识别为msdos), * 所以使用linux接口获取。 */ static gchar* get_fs_type (char* path) { char* fstype = NULL; struct mntent* m = NULL; FILE* f = NULL; f = setmntent ("/etc/mtab", "r"); if (!f) { qDebug() << "get filesystem type error"; } while ((m = getmntent(f))) { if (!path) continue; if (!m->mnt_dir) continue; if (!strcmp (path, m->mnt_dir)) { fstype = g_strdup_printf("%s", m->mnt_type); break; } } endmntent (f); return fstype; } peony/libpeony-qt/effects/0000755000175000017500000000000014205101223014500 5ustar fengfengpeony/libpeony-qt/effects/border-shadow-effect.h0000644000175000017500000000531714205101223020651 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #ifndef BORDERSHADOWEFFECT_H #define BORDERSHADOWEFFECT_H #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) #include #endif /*! * \brief The BorderShadowEffect class * \details * This class is used to decorate a frameless window. * It provides a border shadow which can be adjusted. * * The effect is similar to QGraphicsDropShadowEffects, * but it doesn't blur allow the window. It just render * a border for toplevel window, whatever if the window * is transparent and whatever element on the window. * * \note * To let the effect works, * You have to use it on toplevel window, and let the * window has an invisible contents margins. * * If your window has a border radius, use setBorderRadius() * for matching your window border rendering. */ class PEONYCORESHARED_EXPORT BorderShadowEffect : public QGraphicsEffect { Q_OBJECT public: explicit BorderShadowEffect(QObject *parent = nullptr); void setBorderRadius(int radius); void setBorderRadius(int xradius, int yradius); void setBlurRadius(int radius); void setPadding(int padding); void setShadowColor(const QColor &color); void setWindowBackground(const QColor &color); void setTransParentPath(const QPainterPath &path); void setTransParentAreaBg(const QColor &transparentBg); void drawWindowShadowManually(QPainter *painter, const QRect &windowRect, bool fakeShadow = false); protected: void draw(QPainter *painter) override; private: int m_x_border_radius = 0; int m_y_border_radius = 0; int m_blur_radius = 0; int m_padding = 0; QColor m_shadow_color = QColor(63, 63, 63, 180); // dark gray QColor m_window_bg = Qt::transparent; QImage m_cache_shadow; bool m_force_update_cache = false; QPainterPath m_transparent_path; QColor m_transparent_bg = QColor(255, 255, 255, 127); }; #endif // BORDERSHADOWEFFECT_H peony/libpeony-qt/effects/effects.pri0000644000175000017500000000016514205101223016635 0ustar fengfengINCLUDEPATH += $$PWD HEADERS += \ $$PWD/border-shadow-effect.h SOURCES += \ $$PWD/border-shadow-effect.cpp peony/libpeony-qt/effects/border-shadow-effect.cpp0000644000175000017500000001346114205101223021203 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #include "border-shadow-effect.h" #include #include #include //qt's global function extern void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed); BorderShadowEffect::BorderShadowEffect(QObject *parent) : QGraphicsEffect(parent) { } void BorderShadowEffect::setBorderRadius(int radius) { m_x_border_radius = 0; m_y_border_radius = 0; //m_x_border_radius = radius; //m_y_border_radius = radius; } void BorderShadowEffect::setBorderRadius(int xradius, int yradius) { m_x_border_radius = 0; m_y_border_radius = 0; //m_x_border_radius = xradius; //m_y_border_radius = yradius; } void BorderShadowEffect::setBlurRadius(int radius) { m_blur_radius = 0; //m_blur_radius = radius; } void BorderShadowEffect::setPadding(int padding) { m_padding = 0; //m_padding = padding; } void BorderShadowEffect::setShadowColor(const QColor &color) { if (color != m_shadow_color) { m_force_update_cache = true; } m_shadow_color = color; } void BorderShadowEffect::setWindowBackground(const QColor &color) { m_window_bg = color; } void BorderShadowEffect::setTransParentPath(const QPainterPath &path) { m_transparent_path = path; } void BorderShadowEffect::setTransParentAreaBg(const QColor &transparentBg) { m_transparent_bg = transparentBg; } void BorderShadowEffect::drawWindowShadowManually(QPainter *painter, const QRect &windowRect, bool fakeShadow) { //draw window bg; QRect sourceRect = windowRect; auto contentRect = sourceRect.adjusted(m_padding, m_padding, -m_padding, -m_padding); //qDebug()<fillPath(bgPath, m_window_bg); painter->fillPath(m_transparent_path, m_transparent_bg); return; if (fakeShadow) { painter->save(); auto color = m_shadow_color; color.setAlphaF(0.1); painter->setPen(color); painter->setBrush(Qt::transparent); painter->drawPath(contentPath); painter->restore(); return; } //qDebug()<boundingRect()< 0) { if (m_cache_shadow.size() == windowRect.size() && !m_force_update_cache) { //use cache pixmap draw shadow. painter->save(); painter->setClipPath(sourcePath - contentPath); painter->drawImage(QPoint(), m_cache_shadow); painter->restore(); } else { //draw shadow and cache shadow pixmap QPixmap pixmap(sourceRect.size().width(), sourceRect.height()); pixmap.fill(Qt::transparent); QPainter p(&pixmap); p.fillPath(contentPath, m_shadow_color); p.end(); QImage img = pixmap.toImage(); qt_blurImage(img, m_blur_radius, false, false); pixmap.convertFromImage(img); m_cache_shadow = img; painter->save(); painter->setClipPath(sourcePath - contentPath); painter->drawImage(QPoint(), img); painter->restore(); m_force_update_cache = false; } } } void BorderShadowEffect::draw(QPainter *painter) { //draw window bg; auto sourceRect = boundingRect(); auto contentRect = boundingRect().adjusted(m_padding, m_padding, -m_padding, -m_padding); //qDebug()<fillPath(contentPath, m_window_bg); QPoint offset; if (sourceIsPixmap()) { // No point in drawing in device coordinates (pixmap will be scaled anyways). const QPixmap pixmap = sourcePixmap(Qt::LogicalCoordinates, &offset, QGraphicsEffect::PadToTransparentBorder); painter->drawPixmap(offset, pixmap); } else { // Draw pixmap in device coordinates to avoid pixmap scaling; const QPixmap pixmap = sourcePixmap(Qt::DeviceCoordinates, &offset, QGraphicsEffect::PadToTransparentBorder); painter->setWorldTransform(QTransform()); painter->drawPixmap(offset, pixmap); } //qDebug()<boundingRect()< 0) { //draw shadow QPixmap pixmap(sourceRect.size().width(), sourceRect.height()); pixmap.fill(Qt::transparent); QPainter p(&pixmap); p.fillPath(contentPath, m_shadow_color); p.end(); QImage img = pixmap.toImage(); qt_blurImage(img, m_blur_radius, false, false); pixmap.convertFromImage(img); painter->save(); painter->setClipPath(sourcePath - contentPath); painter->drawImage(QPoint(), img); painter->restore(); } } peony/libpeony-qt/vfs/0000755000175000017500000000000014205115226013667 5ustar fengfengpeony/libpeony-qt/vfs/favorite-vfs-file-enumerator.h0000644000175000017500000000307514205101223021544 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Ding Jing * */ #ifndef FAVORITEVFSFILEENUMERATOR_H #define FAVORITEVFSFILEENUMERATOR_H #include #include G_BEGIN_DECLS #define VFS_TYPE_FAVORITES_FILE_ENUMERATOR vfs_favorites_file_enumerator_get_type() G_DECLARE_FINAL_TYPE(FavoritesVFSFileEnumerator, vfs_favorites_file_enumerator, VFS, FAVORITES_FILE_ENUMERATOR, GFileEnumerator) FavoritesVFSFileEnumerator* vfs_favorites_file_enumerator_new(void); typedef struct _FavoritesVFSFileEnumeratorPrivate FavoritesVFSFileEnumeratorPrivate; struct _FavoritesVFSFileEnumeratorPrivate { QQueue* enumerate_queue; }; struct _FavoritesVFSFileEnumerator { GFileEnumerator parent_instance; FavoritesVFSFileEnumeratorPrivate* priv; }; G_END_DECLS #endif // FAVORITEVFSFILEENUMERATOR_H peony/libpeony-qt/vfs/search-vfs-uri-parser.h0000644000175000017500000000265314205101223020166 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef SEARCHVFSURIPARSER_H #define SEARCHVFSURIPARSER_H #include #include namespace Peony { class PEONYCORESHARED_EXPORT SearchVFSUriParser { public: const static QString parseSearchKey(const QString &uri, const QString &key, const bool &search_file_name=true, const bool &search_content=false, const QString &extend_key="", const bool &recursive = true); const static QString getSearchUriNameRegexp(const QString &searchUri); const static QString getSearchUriTargetDirectory(const QString &searchUri); private: SearchVFSUriParser(); }; } #endif // SEARCHVFSURIPARSER_H peony/libpeony-qt/vfs/favorite-vfs-file.cpp0000644000175000017500000005762514205115226017742 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Ding Jing * */ #include "file-utils.h" #include "favorite-vfs-file.h" #include "favorite-vfs-file-monitor.h" #include "favorite-vfs-file-enumerator.h" #include #include #include #include static char* vfs_favorite_file_get_target_file (GFile* file); static void vfs_favorite_file_g_file_iface_init(GFileIface *iface); GFile* vfs_favorite_file_dup(GFile *file); char* vfs_favorite_file_get_uri(GFile *file); char* vfs_favorite_file_get_path(GFile *file); gboolean vfs_favorite_file_is_native(GFile *file); GFile* vfs_favorite_file_get_parent(GFile *file); char* vfs_favorite_file_get_schema (GFile* file); void vfs_favorite_file_dispose(GObject *object); char* vfs_favorite_file_get_basename (GFile* file); GFile* vfs_favorite_file_new_for_uri(const char *uri); gboolean vfs_favorite_file_is_equal(GFile *file1, GFile *file2); char* vfs_favorite_file_get_relative_path (GFile* parent, GFile* descendant); GFile* vfs_favorite_file_resolve_relative_path(GFile *file, const char *relative_path); GFileInputStream* vfs_favorite_file_read_fn(GFile* file, GCancellable* cancellable, GError** error); gboolean vfs_favorite_file_delete (GFile* file, GCancellable* cancellable, GError** error); GFileIOStream * vfs_favorite_file_open_readwrite(GFile* file, GCancellable* cancellable, GError** error); gboolean vfs_favorite_file_make_directory(GFile* file, GCancellable* cancellable, GError** error); GMount * vfs_favorite_file_find_enclosing_mount(GFile* file, GCancellable* cancellable, GError** error); GFileOutputStream* vfs_favorite_file_create(GFile* file, GFileCreateFlags flags, GCancellable* cancellable, GError** error); gboolean vfs_favorite_file_make_symbolic_link(GFile* file, const char* svalue, GCancellable* cancellable, GError** error); GFileMonitor* vfs_favorite_file_monitor_directory (GFile* file, GFileMonitorFlags flags, GCancellable* cancellable, GError** error); GFile* vfs_favorite_file_set_display_name (GFile* file, const gchar* display_name, GCancellable* cancellable, GError** error); GFileInfo* vfs_favorite_file_query_filesystem_info(GFile* file, const char* attributes, GCancellable* cancellable, GError** error); GFileInfo* vfs_favorite_file_query_info(GFile *file, const char *attributes, GFileQueryInfoFlags flags, GCancellable *cancellable, GError **error); GFileEnumerator* vfs_favorite_file_enumerate_children(GFile *file, const char *attribute, GFileQueryInfoFlags flags, GCancellable *cancellable, GError **error); GFileOutputStream* vfs_favorite_file_replace(GFile* file, const char* etag, gboolean make_backup, GFileCreateFlags flags, GCancellable* cancellable, GError** error); GFileEnumerator* vfs_favorite_file_enumerate_children_internal(GFile *file, const char *attribute, GFileQueryInfoFlags flags, GCancellable *cancellable, GError **error); gboolean vfs_favorite_file_copy(GFile* source, GFile* destination, GFileCopyFlags flags, GCancellable* cancellable, GFileProgressCallback pcallback, gpointer pcallbackdata, GError** error); gboolean vfs_favorite_file_move(GFile* source, GFile* destination, GFileCopyFlags flags, GCancellable* cancellable, GFileProgressCallback progress_callback, gpointer progress, GError** error); G_DEFINE_TYPE_EXTENDED(FavoriteVFSFile, vfs_favorite_file, G_TYPE_OBJECT, 0, G_ADD_PRIVATE(FavoriteVFSFile) G_IMPLEMENT_INTERFACE(G_TYPE_FILE, vfs_favorite_file_g_file_iface_init)); static void vfs_favorite_file_init(FavoriteVFSFile* self) { g_return_if_fail(VFS_IS_FAVORITES_FILE(self)); FavoriteVFSFilePrivate *priv = (FavoriteVFSFilePrivate*)vfs_favorite_file_get_instance_private(self); self->priv = priv; self->priv->uri = nullptr; } static void vfs_favorite_file_class_init (FavoriteVFSFileClass* kclass) { GObjectClass* gobject_class = G_OBJECT_CLASS (kclass); gobject_class->dispose = vfs_favorite_file_dispose; } void vfs_favorite_file_dispose(GObject *object) { g_return_if_fail(VFS_IS_FAVORITES_FILE(object)); auto vfsfile = VFS_FAVORITES_FILE (object); if (G_IS_FILE_MONITOR(vfsfile->priv->fileMonitor)) { g_file_monitor_cancel (vfsfile->priv->fileMonitor); vfsfile->priv->fileMonitor = nullptr; } if (vfsfile->priv->uri) { g_free(vfsfile->priv->uri); vfsfile->priv->uri = nullptr; } } static void vfs_favorite_file_g_file_iface_init(GFileIface *iface) { iface->dup = vfs_favorite_file_dup; iface->move = vfs_favorite_file_move; iface->copy = vfs_favorite_file_copy; iface->trash = vfs_favorite_file_delete; iface->read_fn = vfs_favorite_file_read_fn; iface->equal = vfs_favorite_file_is_equal; iface->create = vfs_favorite_file_create; iface->get_uri = vfs_favorite_file_get_uri; iface->get_path = vfs_favorite_file_get_path; iface->is_native = vfs_favorite_file_is_native; iface->get_parent = vfs_favorite_file_get_parent; iface->query_info = vfs_favorite_file_query_info; iface->delete_file = vfs_favorite_file_delete; iface->get_uri_scheme = vfs_favorite_file_get_schema; iface->get_basename = vfs_favorite_file_get_basename; iface->open_readwrite = vfs_favorite_file_open_readwrite; iface->make_directory = vfs_favorite_file_make_directory; iface->set_display_name = vfs_favorite_file_set_display_name; iface->get_relative_path = vfs_favorite_file_get_relative_path; iface->monitor_dir = vfs_favorite_file_monitor_directory; iface->make_symbolic_link = vfs_favorite_file_make_symbolic_link; iface->enumerate_children = vfs_favorite_file_enumerate_children; iface->find_enclosing_mount = vfs_favorite_file_find_enclosing_mount; iface->query_filesystem_info = vfs_favorite_file_query_filesystem_info; iface->resolve_relative_path = vfs_favorite_file_resolve_relative_path; } char* vfs_favorite_file_get_uri(GFile *file) { g_return_val_if_fail(VFS_IS_FAVORITES_FILE(file), g_strdup("favorite:///")); auto vfsfile = VFS_FAVORITES_FILE (file); return g_strdup(Peony::FileUtils::urlDecode(vfsfile->priv->uri).toUtf8().constData()); } gboolean vfs_favorite_file_is_native(GFile *file) { Q_UNUSED(file); return FALSE; } GFile* vfs_favorite_file_dup (GFile *file) { g_return_val_if_fail(VFS_IS_FAVORITES_FILE(file), g_file_new_for_uri("favorite:///")); auto vfs_file = VFS_FAVORITES_FILE(file); auto dup = VFS_FAVORITES_FILE(g_object_new(VFS_TYPE_FAVORITE_FILE, nullptr)); dup->priv->uri = g_strdup(vfs_file->priv->uri); return G_FILE(dup); } GFileEnumerator* vfs_favorite_file_enumerate_children_internal(GFile *file, const char *attribute, GFileQueryInfoFlags flags, GCancellable *cancellable, GError **error) { g_return_val_if_fail(VFS_IS_FAVORITES_FILE(file), nullptr); Q_UNUSED(flags) Q_UNUSED(error) Q_UNUSED(attribute) Q_UNUSED(cancellable) QString uri = g_file_get_uri(file); if ("favorite:///" == uri) { return G_FILE_ENUMERATOR(g_object_new(VFS_TYPE_FAVORITES_FILE_ENUMERATOR, "container", file, nullptr)); } return nullptr; } GFile* vfs_favorite_file_new_for_uri(const char* turi) { auto vfsfile = VFS_FAVORITES_FILE(g_object_new(VFS_TYPE_FAVORITE_FILE, nullptr)); QString quri; QString uri = Peony::FileUtils::urlDecode(turi); QString urii1 = uri.replace("favorite://", ""); QStringList path1 = urii1.split("/"); QStringList path2; QString path3; QString schemaInfo; for (auto i : path1) { if ("" != i) { if (!i.contains("?schema=")) { path2 << i; continue; } QStringList tmp = i.split("?schema="); if (tmp.size() >= 2 && "favorite" != tmp[1]) { path2 << tmp[0]; schemaInfo = "?schema=" + tmp[1]; } } } path3 = path2.join("/") + schemaInfo; if ("?schema=" == schemaInfo || "" == path3) { quri = "favorite:///"; } else { quri = "favorite:///" + path2.join("/") + schemaInfo; } vfsfile->priv->uri = g_strdup(Peony::FileUtils::urlEncode(quri).toUtf8().constData()); return G_FILE(vfsfile); } char* vfs_favorite_file_get_path(GFile *file) { return nullptr; g_return_val_if_fail(VFS_IS_FAVORITES_FILE(file), nullptr); QUrl url = QString(vfs_favorite_file_get_uri (file)); return g_strdup (url.path().toUtf8().constData()); } GFile* vfs_favorite_file_resolve_relative_path(GFile *file, const char *relativepath) { QString tmp = relativepath; if (tmp.contains('/')) { if (relativepath) { return g_file_new_for_uri(relativepath); } } else if (!tmp.isEmpty()) { QString uri = "favorite:///" + tmp; return vfs_favorite_file_new_for_uri(uri.toUtf8().constData()); } return g_file_new_for_uri("favorite:///");; } GFileInfo* vfs_favorite_file_query_info(GFile *file, const char *attributes, GFileQueryInfoFlags flags, GCancellable *cancellable, GError **error) { g_return_val_if_fail(VFS_IS_FAVORITES_FILE(file), nullptr); auto vfsfile = VFS_FAVORITES_FILE(file); GFileInfo* info = nullptr; QString trueUri = nullptr; QUrl url(vfs_favorite_file_get_uri(file)); if ("favorite:///" != url.toString()) { QStringList querys = url.query().split("&"); for (int i = 0; i < querys.count(); ++i) { if (querys.at(i).contains("schema=")) { QString scheam = querys.at(i).split("=").last(); if ("favorite" == scheam) { trueUri = QString("%1://%2").arg("file").arg(url.path()); } else { trueUri = QString("%1://%2").arg(scheam).arg(url.path()); } break; } } } if (nullptr != trueUri) { GFile* file = g_file_new_for_uri (trueUri.toUtf8().constData()); if (nullptr != file) { info = g_file_query_info(file, "*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); if (!info) info = g_file_info_new(); g_file_info_set_attribute_string(info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, trueUri.toUtf8().constData()); } else { info = g_file_info_new(); } g_object_unref(file); } else { info = g_file_info_new (); QString name = QObject::tr("favorite"); auto icon = g_themed_icon_new("favorite"); g_file_info_set_icon(info, icon); g_object_unref(icon); g_file_info_set_size(info, 0); g_file_info_set_is_hidden(info, FALSE); g_file_info_set_is_symlink(info, FALSE); g_file_info_set_file_type(info, G_FILE_TYPE_DIRECTORY); g_file_info_set_display_name(info, name.toUtf8().constData()); } g_file_info_set_name (info, vfsfile->priv->uri); if (url.path() == "/data/usershare") { g_file_info_set_attribute_boolean(info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE); g_file_info_set_attribute_string(info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, QObject::tr("Share Data").toUtf8()); } g_file_info_set_attribute_boolean(info, G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL, TRUE); g_file_info_set_attribute_boolean(info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, TRUE); g_file_info_set_attribute_boolean(info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE); g_file_info_set_attribute_boolean(info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, FALSE); g_file_info_set_attribute_boolean(info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, FALSE); Q_UNUSED(flags) Q_UNUSED(error) Q_UNUSED(attributes) Q_UNUSED(cancellable) return info; } GFileEnumerator* vfs_favorite_file_enumerate_children(GFile *file, const char *attribute, GFileQueryInfoFlags flags, GCancellable *cancellable, GError **error) { g_return_val_if_fail(VFS_IS_FAVORITES_FILE(file), nullptr); return vfs_favorite_file_enumerate_children_internal(file, attribute, flags, cancellable, error); } GFileMonitor* vfs_favorite_file_monitor_directory (GFile* file, GFileMonitorFlags flags, GCancellable* cancellable, GError** error) { g_return_val_if_fail(VFS_IS_FAVORITES_FILE(file), nullptr); FavoriteVFSFilePrivate* priv = VFS_FAVORITE_FILE((FavoriteVFSFile*)file)->priv; priv->fileMonitor = (GFileMonitor*) g_object_new (VFS_TYPE_FAVORITE_FILE_MONITOR, nullptr); vfs_favorite_file_monitor_dir (VFS_FAVORITE_FILE_MONITOR(priv->fileMonitor)); Q_UNUSED(file) Q_UNUSED(flags) Q_UNUSED(error) Q_UNUSED(cancellable) return priv->fileMonitor; } gboolean vfs_favorite_file_delete (GFile* file, GCancellable* cancellable, GError** error) { QString uri = nullptr; GFileIface *iface = nullptr; g_return_val_if_fail (VFS_IS_FAVORITES_FILE(file), FALSE); if (g_cancellable_set_error_if_cancelled (cancellable, error)) { return FALSE; } iface = G_FILE_GET_IFACE (file); uri = g_file_get_uri (file); QString errorStr = QObject::tr("Operation not supported"); if (iface->delete_file == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, errorStr.toUtf8().constData()); return FALSE; } Peony::BookMarkManager::getInstance()->removeBookMark(uri); return TRUE; } char* vfs_favorite_file_get_schema (GFile* file) { Q_UNUSED(file); return g_strdup("favorite"); } GFile* vfs_favorite_file_get_parent (GFile* file) { g_return_val_if_fail(VFS_IS_FAVORITES_FILE(file), nullptr); Q_UNUSED(file); QString uri = g_file_get_uri(file); if ("favorite:///" == uri) { return nullptr; } return g_file_new_for_uri("favorite:///"); } char* vfs_favorite_file_get_basename (GFile* file) { g_return_val_if_fail(VFS_IS_FAVORITES_FILE(file), nullptr); char* uri = vfs_favorite_file_get_uri (file); QString baseName = nullptr; if (0 == strcmp("favorite:///", uri)) { return g_strdup("favorite:///"); } else { QString url = uri; QString baseNamet = url.split("/").takeLast(); if (baseNamet.contains("?schema=") && baseNamet.split("?schema=").size() >= 2) { baseName = baseNamet.split("?schema=").first(); } } if (nullptr != uri) g_free(uri); return g_strdup (baseName.toUtf8().constData()); } gboolean vfs_favorite_file_is_equal(GFile *file1, GFile* file2) { g_return_val_if_fail(VFS_IS_FAVORITES_FILE(file1) || VFS_IS_FAVORITES_FILE(file2), false); char* f1 = g_file_get_uri(file1); char* f2 = g_file_get_uri(file2); int ret = g_strcmp0(f1, f2); if (nullptr != f1) { g_free(f1); } if (nullptr != f2) { g_free(f2); } return ret == 0; } GFileOutputStream* vfs_favorite_file_create(GFile* file, GFileCreateFlags flags, GCancellable* cancellable, GError** error) { Q_UNUSED(file); Q_UNUSED(flags); Q_UNUSED(error); Q_UNUSED(cancellable); // fixme:// Do not implement QString str = QObject::tr("Virtual file directories do not support move and copy operations"); *error = g_error_new(G_FILE_ERROR_FAILED, G_IO_ERROR_NOT_SUPPORTED, "%s\n", str.toUtf8().constData()); return FALSE; } gboolean vfs_favorite_file_make_directory(GFile* file, GCancellable* cancellable, GError** error) { Q_UNUSED(file); Q_UNUSED(error); Q_UNUSED(cancellable); QString str = QObject::tr("The virtual file system does not support folder creation"); *error = g_error_new(G_FILE_ERROR_FAILED, G_IO_ERROR_NOT_SUPPORTED, "%s\n", str.toUtf8().constData()); return FALSE; } gboolean vfs_favorite_file_make_symbolic_link(GFile* file, const char* svalue, GCancellable* cancellable, GError** error) { Q_UNUSED(file); Q_UNUSED(error); Q_UNUSED(svalue); Q_UNUSED(cancellable); // fixme:// Do not implement QString str = QObject::tr("Virtual file directories do not support move and copy operations"); *error = g_error_new(G_FILE_ERROR_FAILED, G_IO_ERROR_NOT_SUPPORTED, "%s\n", str.toUtf8().constData()); return FALSE; } gboolean vfs_favorite_file_move(GFile* source, GFile* destination, GFileCopyFlags flags, GCancellable* cancellable, GFileProgressCallback progress_callback, gpointer progress, GError** error) { Q_UNUSED(flags); Q_UNUSED(progress); Q_UNUSED(cancellable); Q_UNUSED(progress_callback); // fixme:// Do not implement QString str = QObject::tr("Virtual file directories do not support move and copy operations"); char *src_uri = g_file_get_uri(source); char *dest_uri = g_file_get_uri(destination); char *src_scheme = g_file_get_uri_scheme(source); char *dest_scheme = g_file_get_uri_scheme(destination); QString srcUri = src_uri; QString destUri = dest_uri; // we have constructed a error dest uri in FileMoveOperation::run(), // at here we should chop the relative path and add it here manually. destUri.chop(destUri.length() - destUri.lastIndexOf('/')); QString srcScheme = src_scheme; QString destScheme = dest_scheme; if (src_uri) { g_free(src_uri); } if (dest_uri) { g_free(dest_uri); } if (src_scheme) { g_free(src_scheme); } if (dest_scheme) { g_free(dest_scheme); } if (srcScheme == destScheme) { // dnd from favorite:/// to favorite:///, do nothing return true; } if (srcUri.startsWith("filesafe:///")){ return true; } if (srcScheme == "favorite" && destScheme == "file") { // dnd from favorite:/// to file:///xxx // find file favorite file point to, and create a symbolic link. auto file_info = g_file_query_info(source, "*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); auto target_uri = g_file_info_get_attribute_as_string(file_info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); QString targetUri = target_uri; if (target_uri) { g_free(target_uri); } g_object_unref(file_info); if (!targetUri.startsWith("file:///")) { *error = g_error_new_literal(G_IO_ERROR, G_IO_ERROR_FAILED, QObject::tr("Can not create a symbolic file for vfs location").toUtf8().constData()); return false; } auto real_src_file = g_file_new_for_uri(targetUri.toUtf8().constData()); auto real_src_path = g_file_get_path(real_src_file); auto base_name = g_file_get_basename(real_src_file); g_object_unref(real_src_file); QString destLinkUri = destUri + "/" + QObject::tr("Symbolic Link") + " - " + base_name; auto symbolic_file = g_file_new_for_uri(destLinkUri.toUtf8().constData()); GError *error2 = nullptr; bool success = g_file_make_symbolic_link(symbolic_file, real_src_path, nullptr, &error2); if (base_name) { g_free(base_name); } if (real_src_path) { g_free(real_src_path); } g_object_unref(symbolic_file); if (!success) { *error = g_error_new_literal(G_IO_ERROR, G_IO_ERROR_FAILED, QObject::tr("Can not create symbolic file here, %1").arg(error2->message).toUtf8().constData()); g_error_free(error2); } return success; } if (srcScheme == "file" && destScheme == "favorite") { // add src file to favorite:/// Peony::BookMarkManager::getInstance()->addBookMark(srcUri); return true; } *error = g_error_new(G_FILE_ERROR_FAILED, G_IO_ERROR_NOT_SUPPORTED, "%s\n", str.toUtf8().constData()); return FALSE; } gboolean vfs_favorite_file_copy(GFile* source, GFile* destination, GFileCopyFlags flags, GCancellable* cancellable, GFileProgressCallback pcallback, gpointer pcallbackdata, GError** error) { // fixme:// Do not implement QString str = QObject::tr("Virtual file directories do not support move and copy operations"); *error = g_error_new(G_FILE_ERROR_FAILED, G_IO_ERROR_NOT_SUPPORTED, "%s\n", str.toUtf8().constData()); return FALSE; } GFile* vfs_favorite_file_set_display_name (GFile* file, const gchar* display_name, GCancellable* cancellable, GError** error) { Q_UNUSED(error); Q_UNUSED(cancellable); Q_UNUSED(display_name); return file; } GFileOutputStream* vfs_favorite_file_replace(GFile* file, const char* etag, gboolean make_backup, GFileCreateFlags flags, GCancellable* cancellable, GError** error) { QString str = QObject::tr("Virtual file directories do not support move and copy operations"); *error = g_error_new(G_FILE_ERROR_FAILED, G_IO_ERROR_NOT_SUPPORTED, "%s\n", str.toUtf8().constData()); return FALSE; } GFileInfo* vfs_favorite_file_query_filesystem_info(GFile* file, const char* attributes, GCancellable* cancellable, GError** error) { return vfs_favorite_file_query_info(file, attributes, G_FILE_QUERY_INFO_NONE, cancellable, error); } GMount* vfs_favorite_file_find_enclosing_mount(GFile* file, GCancellable* cancellable, GError** error) { return nullptr; } char* vfs_favorite_file_get_relative_path (GFile* parent, GFile* descendant) { return nullptr; } GFileInputStream* vfs_favorite_file_read_fn(GFile* file, GCancellable* cancellable, GError** error) { QString str = QObject::tr("The virtual file system cannot be opened"); *error = g_error_new(G_FILE_ERROR_FAILED, G_IO_ERROR_NOT_SUPPORTED, "%s\n", str.toUtf8().constData()); return nullptr; } GFileIOStream* vfs_favorite_file_open_readwrite(GFile* file, GCancellable* cancellable, GError** error) { QString str = QObject::tr("The virtual file system cannot be opened"); *error = g_error_new(G_FILE_ERROR_FAILED, G_IO_ERROR_NOT_SUPPORTED, "%s\n", str.toUtf8().constData()); return nullptr; } gboolean vfs_favorite_file_is_exist(const char *uri) { gboolean ret = FALSE; GFile* file = g_file_new_for_uri(uri); GFileInfo* fileInfo = nullptr; if (nullptr != file) { fileInfo = g_file_query_info(file, "*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); if (nullptr != fileInfo) { gchar* targetUri = g_file_info_get_attribute_as_string(fileInfo, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); ret = Peony::FileUtils::isFileExsit(targetUri); g_free(targetUri); g_object_unref(fileInfo); } g_object_unref(file); } return ret; } static char* vfs_favorite_file_get_target_file (GFile* file) { if (nullptr == file) { return nullptr; } gchar* uri = nullptr; GFileInfo* fileInfo = nullptr; gchar* targetUri = nullptr; uri = g_file_get_uri(file); if (nullptr != uri && 0 != strncmp("favorite://", uri, 11)) { return uri; } if (nullptr != uri) g_free(uri); fileInfo = g_file_query_info(file, "*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); if (nullptr != fileInfo) { targetUri = g_file_info_get_attribute_as_string(fileInfo, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); g_object_unref(fileInfo); } return targetUri; } peony/libpeony-qt/vfs/vfs-plugin-manager.h0000644000175000017500000000325014205101223017532 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef VFSPLUGINMANAGER_H #define VFSPLUGINMANAGER_H #include "vfs-plugin-iface.h" #include #include namespace Peony { class PEONYCORESHARED_EXPORT VFSPluginManager : public QObject { Q_OBJECT public: static VFSPluginManager *getInstance(); void registerPlugin(VFSPluginIface *plugin); QList registeredPlugins(); /*! * \brief supportExtraSchemes * \return the registered plugins schemes, like "search://". */ const QStringList supportExtraSchemes(); /*! * \brief newVFSFile * construct a vfs file implemented by plugin. * \param uri * \return * \see supportExtraSchemes */ GFile *newVFSFile(const QString &uri); private: explicit VFSPluginManager(QObject *parent = nullptr); QList m_plugins; QStringList m_support_schemes; }; } #endif // VFSPLUGINMANAGER_H peony/libpeony-qt/vfs/search-vfs-uri-parser.cpp0000644000175000017500000000720014205113763020526 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * Authors: Meihong He * */ #include "search-vfs-uri-parser.h" #include "file-utils.h" #include #include using namespace Peony; SearchVFSUriParser::SearchVFSUriParser() { } const QString SearchVFSUriParser:: parseSearchKey(const QString &uri, const QString &key, const bool &search_file_name, const bool &search_content, const QString &extend_key, const bool &recursive) { QString search_str = "search:///search_uris="+uri; if (search_file_name) search_str += "&name_regexp="+key; if (search_content) search_str += "&content_regexp="+key; //multiple name key search if (extend_key != "") search_str += "&extend_regexp="+extend_key; else if (! search_file_name && ! search_content && extend_key=="") { qWarning()<<"Search content or file name at least one be true!"; //use default search file name search_str += "&name_regexp="+key; } if (recursive) return QString(search_str+"&recursive=1"); return QString(search_str+"&recursive=0"); } const QString SearchVFSUriParser::getSearchUriNameRegexp(const QString &searchUri) { auto string = searchUri; string.remove("search:///"); auto list = string.split("&"); qDebug() << "list length:" <. * * Authors: Yue Lan * */ #include "search-vfs-manager.h" using namespace Peony; static SearchVFSManager* global_manager = nullptr; SearchVFSManager *SearchVFSManager::getInstance() { if (!global_manager) { global_manager = new SearchVFSManager; } return global_manager; } SearchVFSManager::SearchVFSManager(QObject *parent) : QObject(parent) { } SearchVFSManager::~SearchVFSManager() { m_search_dir_results_hash.clear(); } void SearchVFSManager::clearHistory() { m_mutex.lock(); m_search_dir_results_hash.clear(); m_mutex.unlock(); } void SearchVFSManager::clearHistoryOne(const QString &searchUri) { m_mutex.lock(); m_search_dir_results_hash.remove(searchUri); m_mutex.unlock(); } bool SearchVFSManager::hasHistory(const QString &searchUri) { return m_search_dir_results_hash.contains(searchUri); } void SearchVFSManager::addHistory(const QString &searchUri, const QStringList &results) { m_mutex.lock(); m_search_dir_results_hash.insert(searchUri, results); m_mutex.unlock(); } QStringList SearchVFSManager::getHistroyResults(const QString &searchUri) { return m_search_dir_results_hash.value(searchUri); } peony/libpeony-qt/vfs/favorite-vfs-file-monitor.h0000644000175000017500000000626614205101223021057 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Ding Jing * */ #ifndef FAVORITEVFSFILEMONITOR_H #define FAVORITEVFSFILEMONITOR_H #include #include G_BEGIN_DECLS #define VFS_FAVORITE_FILE_MONITOR_NAME ("vfs-favorite-file-monitor") #define VFS_TYPE_FAVORITE_FILE_MONITOR (vfs_favorite_file_monitor_get_type()) #define VFS_FAVORITE_FILE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), VFS_TYPE_FAVORITE_FILE_MONITOR, FavoriteVFSFileMonitor)) #define VFS_FAVORITE_FILE_MONITOR_CLASS(k) (G_TYPE_CLASS_CAST((k), VFS_TYPE_FAVORITE_FILE_MONITOR, FavoriteVFSFileMonitorClass)) #define VFS_IS_FAVORITE_FILE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), VFS_TYPE_FAVORITE_FILE_MONITOR)) #define VFS_IS_FAVORITE_FILE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), VFS_TYPE_FAVORITE_FILE_MONITOR)) #define VFS_FAVORITE_FILE_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), VFS_TYPE_FAVORITE_FILE_MONITOR, FavoriteVFSFileMonitorClass)) typedef struct _FavoriteVFSFileMonitor FavoriteVFSFileMonitor; typedef struct _FavoriteVFSFileMonitorClass FavoriteVFSFileMonitorClass; typedef struct _FavoriteVFSFileMonitorSource FavoriteVFSFileMonitorSource; typedef struct _FavoriteVFSFileMonitorPrivate FavoriteVFSFileMonitorPrivate; struct _FavoriteVFSFileMonitor { GFileMonitor parent_instance; FavoriteVFSFileMonitorPrivate* priv; FavoriteVFSFileMonitorSource* source; GList* fileList; QMetaObject::Connection add; QMetaObject::Connection remove; }; struct _FavoriteVFSFileMonitorClass { GFileMonitorClass parent_class; void (*start) (FavoriteVFSFileMonitor*, const gchar*, const gchar*, const gchar*, FavoriteVFSFileMonitorSource*); }; GType vfs_favorite_file_monitor_get_type (void); void vfs_favorite_file_monitor_dir (FavoriteVFSFileMonitor* obj); void vfs_favorite_file_monitor_free_gfile (FavoriteVFSFileMonitor* obj, GFile*); FavoriteVFSFileMonitor* vfs_favorite_file_monitor_new_for_path (const char* path, gboolean isDirectory, GFileMonitorFlags flags, GError**); gboolean vfs_favorite_file_monitor_source_handle_event (FavoriteVFSFileMonitorSource*, GFileMonitorEvent, const gchar* child, const gchar* renameTo, GFile* other, gint64 eTime); G_END_DECLS #endif // FAVORITEVFSFILEMONITOR_H peony/libpeony-qt/vfs/favorite-vfs-register.cpp0000644000175000017500000000406214205101223020622 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Ding Jing * */ #include "favorite-vfs-file.h" #include "favorite-vfs-register.h" #include "favorite-vfs-file-enumerator.h" #include #include using namespace Peony; bool favorite_is_registed = false; static GFile* test_vfs_parse_name (GVfs* vfs, const char* parseName, gpointer udata) { return vfs_favorite_file_new_for_uri(parseName); } static GFile * test_vfs_lookup (GVfs* vfs, const char* uri, gpointer udata) { return test_vfs_parse_name(vfs, uri, udata); } void Peony::FavoriteVFSRegister::registFavoriteVFS() { if (favorite_is_registed) { return; } GVfs* vfs = nullptr; gboolean res = false; const gchar * const *schemes; vfs = g_vfs_get_default (); schemes = g_vfs_get_supported_uri_schemes (vfs); const gchar* const* p = schemes; while (*p) { qDebug() << *p; p++; } #if GLIB_CHECK_VERSION(2, 50, 0) res = g_vfs_register_uri_scheme (vfs, "favorite", test_vfs_lookup, NULL, NULL, test_vfs_parse_name, NULL, NULL); #else #endif } Peony::FavoriteVFSRegister::FavoriteVFSRegister() { } void FavoriteVFSInternalPlugin::initVFS() { FavoriteVFSRegister::registFavoriteVFS(); } void* FavoriteVFSInternalPlugin::parseUriToVFSFile(const QString &uri) { return vfs_favorite_file_new_for_uri(uri.toUtf8()); } peony/libpeony-qt/vfs/vfs.pri0000644000175000017500000000305314205101223015172 0ustar fengfengINCLUDEPATH += \ $$PWD \ $$PWD/.. HEADERS += \ $$PWD/favorite-vfs-file.h \ $$PWD/search-vfs-manager.h \ $$PWD/vfs-plugin-manager.h \ $$PWD/recent-vfs-manager.h \ $$PWD/search-vfs-register.h \ $$PWD/peony-search-vfs-file.h \ $$PWD/search-vfs-uri-parser.h \ $$PWD/favorite-vfs-register.h \ $$PWD/favorite-vfs-file-monitor.h \ $$PWD/favorite-vfs-file-enumerator.h \ $$PWD/peony-search-vfs-file-enumerator.h \ SOURCES += \ $$PWD/favorite-vfs-file.cpp \ $$PWD/search-vfs-manager.cpp \ $$PWD/vfs-plugin-manager.cpp \ $$PWD/recent-vfs-manager.cpp \ $$PWD/search-vfs-register.cpp \ $$PWD/search-vfs-uri-parser.cpp \ $$PWD/peony-search-vfs-file.cpp \ $$PWD/favorite-vfs-register.cpp \ $$PWD/favorite-vfs-file-monitor.cpp \ $$PWD/favorite-vfs-file-enumerator.cpp \ $$PWD/peony-search-vfs-file-enumerator.cpp \ peony/libpeony-qt/vfs/favorite-vfs-register.h0000644000175000017500000000352014205101223020265 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Ding Jing * */ #ifndef FAVORITEVFSREGISTER_H #define FAVORITEVFSREGISTER_H #include "peony-core_global.h" #include "vfs-plugin-iface.h" namespace Peony { class FavoriteVFSInternalPlugin : public VFSPluginIface { public: FavoriteVFSInternalPlugin () {} virtual PluginType pluginType () override {return VFSPlugin;} virtual void setEnable (bool enable) {} virtual bool isEnable () {return true;} virtual const QIcon icon () override {return QIcon();} virtual const QString name () override {return "favorite vfs";} virtual const QString description () override {return QObject::tr("Default favorite vfs of peony");} void initVFS () override; bool holdInSideBar () override {return false;} QString uriScheme () override {return "favorite://";} void* parseUriToVFSFile (const QString &uri) override; CustomErrorHandler *customErrorHandler() override {return nullptr;} }; class PEONYCORESHARED_EXPORT FavoriteVFSRegister { public: static void registFavoriteVFS (); private: FavoriteVFSRegister (); }; } #endif // FAVORITEVFSREGISTER_H peony/libpeony-qt/vfs/favorite-vfs-file.h0000644000175000017500000000451314205101223017363 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Ding Jing * */ #ifndef FAVORITEVFSFILE_H #define FAVORITEVFSFILE_H #include #include #include G_BEGIN_DECLS #define VFS_TYPE_FAVORITE_FILE (vfs_favorite_file_get_type()) #define VFS_IS_FAVORITE_FILE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), VFS_TYPE_FAVORITE_FILE)) #define VFS_IS_FAVORITE_FILE(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), VFS_TYPE_FAVORITE_FILE)) #define VFS_FAVORITE_FILE_CLASS(k) (G_TYPE_CLASS_CAST((k), VFS_TYPE_FAVORITE_FILE, FavoriteVFSFileClass)) #define VFS_FAVORITE_FILE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), VFS_TYPE_FAVORITE_FILE, FavoriteVFSFile)) #define VFS_FAVORITE_FILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), VFS_TYPE_FAVORITE_FILE, FavoriteVFSFileClass)) G_DECLARE_FINAL_TYPE(FavoriteVFSFile, vfs_favorite_file, VFS, FAVORITES_FILE, GObject) FavoriteVFSFile* vfs_favorite_file_new(void); typedef struct _FavoriteVFSFilePrivate FavoriteVFSFilePrivate; struct _FavoriteVFSFilePrivate { gchar* uri; GFileMonitor* fileMonitor; }; struct _FavoriteVFSFile { GObject parent_instance; FavoriteVFSFilePrivate* priv; }; GFile* vfs_favorite_file_new_for_uri (const char* uri); gboolean vfs_favorite_file_is_exist (const char* uri); static GFileEnumerator* vfs_favorite_file_enumerate_children_internal (GFile* file, const char* attribute, GFileQueryInfoFlags flags, GCancellable* cancellable, GError** error); G_END_DECLS #endif // FAVORITEVFSFILE_H peony/libpeony-qt/vfs/peony-search-vfs-file-enumerator.h0000644000175000017500000000367014205101223022323 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef PEONYSEARCHVFSFILEENUMERATOR_H #define PEONYSEARCHVFSFILEENUMERATOR_H #include #include #include #include "file-info.h" G_BEGIN_DECLS #define PEONY_TYPE_SEARCH_VFS_FILE_ENUMERATOR peony_search_vfs_file_enumerator_get_type() G_DECLARE_FINAL_TYPE(PeonySearchVFSFileEnumerator, peony_search_vfs_file_enumerator, PEONY, SEARCH_VFS_FILE_ENUMERATOR, GFileEnumerator) PeonySearchVFSFileEnumerator *peony_search_vfs_file_enumerator_new(void); typedef struct { QString *search_vfs_directory_uri; /*! * \brief search_hidden * \deprecated */ gboolean search_hidden; gboolean use_regexp; gboolean save_result; gboolean recursive; gboolean case_sensitive; QRegExp *name_regexp; QRegExp *content_regexp; QList *name_regexp_extend_list; gboolean match_name_or_content; QQueue *enumerate_queue; } PeonySearchVFSFileEnumeratorPrivate; struct _PeonySearchVFSFileEnumerator { GFileEnumerator parent_instance; PeonySearchVFSFileEnumeratorPrivate *priv; }; G_END_DECLS #endif // PEONYSEARCHVFSFILEENUMERATOR_H peony/libpeony-qt/vfs/peony-search-vfs-file.cpp0000644000175000017500000002631314205115226020506 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "peony-search-vfs-file.h" #include "peony-search-vfs-file-enumerator.h" #include "file-enumerator.h" #include "search-vfs-manager.h" #include #include /* -- GFileIface -- */ static void peony_search_vfs_file_g_file_iface_init(GFileIface *iface); static GFile *peony_search_vfs_file_dup(GFile *file); static guint peony_search_vfs_file_hash(GFile *file);//unused static gboolean peony_search_vfs_file_equal(GFile *file1, GFile *file2);//unused static gboolean peony_search_vfs_file_is_native(GFile *file);//unused static gboolean peony_search_vfs_file_has_uri_scheme(GFile *file, const char *uri_scheme);//unused static char *peony_search_vfs_file_get_uri_sheceme(GFile *file);//unused static char *peony_search_vfs_file_get_basename(GFile *file);//unused static char *peony_search_vfs_file_get_path(GFile *file);//unused static char *peony_search_vfs_file_get_uri(GFile *file); static char *peony_search_vfs_file_get_parse_name(GFile *file);//unused static GFile *peony_search_vfs_file_get_parent(GFile *file);//unused static gboolean peony_search_vfs_file_prefix_matches(GFile *prefix, GFile *file);//unused static char *peony_search_vfs_file_get_relative_path(GFile *file, const char *relative_path);//unused static GFile *peony_search_vfs_file_resolve_relative_path(GFile *file, const char *relative_path); static GFile *peony_search_vfs_file_get_child_for_display_name (GFile *file, const char *display_name, GError **err);//unused static GFileInfo *peony_search_vfs_file_query_info(GFile *file, const char *attributes, GFileQueryInfoFlags flags, GCancellable *cancellable, GError **error); /* -- Implement Iface -- */ G_DEFINE_TYPE_EXTENDED(PeonySearchVFSFile, peony_search_vfs_file, G_TYPE_OBJECT, 0, G_ADD_PRIVATE(PeonySearchVFSFile) G_IMPLEMENT_INTERFACE(G_TYPE_FILE, peony_search_vfs_file_g_file_iface_init)); static void file_dispose(GObject *object) { auto vfs_file = PEONY_SEARCH_VFS_FILE(object); if (vfs_file->priv->uri) { g_free(vfs_file->priv->uri); } } static void peony_search_vfs_file_class_init (PeonySearchVFSFileClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); gobject_class->dispose = file_dispose; } GFileEnumerator *peony_search_vfs_file_enumerate_children(GFile *file, const char *attribute, GFileQueryInfoFlags flags, GCancellable *cancellable, GError **error) { auto search_vfs_file = PEONY_SEARCH_VFS_FILE(file); return peony_search_vfs_file_enumerate_children_internal(file, attribute, flags, cancellable, error); } GFileInfo *peony_search_vfs_file_query_info(GFile *file, const char *attributes, GFileQueryInfoFlags flags, GCancellable *cancellable, GError **error) { auto vfs_file = PEONY_SEARCH_VFS_FILE(file); GFileInfo *info = g_file_info_new(); g_file_info_set_name(info, vfs_file->priv->uri); auto icon = g_themed_icon_new("search"); g_file_info_set_icon(info, icon); g_object_unref(icon); g_file_info_set_display_name(info, " "); g_file_info_set_file_type(info, G_FILE_TYPE_DIRECTORY); return info; } static void peony_search_vfs_file_init(PeonySearchVFSFile *self) { PeonySearchVFSFilePrivate *priv = (PeonySearchVFSFilePrivate*)peony_search_vfs_file_get_instance_private(self); self->priv = priv; priv->uri = nullptr; } char *peony_search_vfs_file_get_uri(GFile *file) { auto vfs_file = PEONY_SEARCH_VFS_FILE(file); return g_strdup(vfs_file->priv->uri); } GFile *peony_search_vfs_file_get_parent(GFile *file) { Q_UNUSED(file); return nullptr; } gboolean peony_search_vfs_file_is_native(GFile *file) { return false; } static void peony_search_vfs_file_g_file_iface_init(GFileIface *iface) { iface->dup = peony_search_vfs_file_dup; iface->get_parent = peony_search_vfs_file_get_parent; iface->is_native = peony_search_vfs_file_is_native; iface->enumerate_children = peony_search_vfs_file_enumerate_children; iface->query_info = peony_search_vfs_file_query_info; iface->get_uri = peony_search_vfs_file_get_uri; iface->get_path = peony_search_vfs_file_get_path; iface->resolve_relative_path = peony_search_vfs_file_resolve_relative_path; } /// dup, get_parent and is_native(and so on) is used in peony-qt, /// such as FileEnumerator and FileInfo, so i must need /// implement these search vfs file's functions. GFile *peony_search_vfs_file_dup(GFile *file) { if (!PEONY_IS_SEARCH_VFS_FILE(file)) { //this should not be happend. return g_file_new_for_uri("search:///"); } auto vfs_file = PEONY_SEARCH_VFS_FILE(file); auto dup = PEONY_SEARCH_VFS_FILE(g_object_new(PEONY_TYPE_SEARCH_VFS_FILE, nullptr)); dup->priv->uri = g_strdup(vfs_file->priv->uri); return G_FILE(dup); } GFile *peony_search_vfs_file_resolve_relative_path(GFile *file, const char *relative_path) { Q_UNUSED(file); //FIXME: maybe i should put the resolve to vfs look up method? QString tmp = relative_path; if (tmp.contains("real-uri:")) { tmp = tmp.remove("real-uri:"); return g_file_new_for_uri(tmp.toUtf8().constData()); } return g_file_new_for_uri("serarch:///"); } GFile *peony_search_vfs_file_new_for_uri(const char *uri) { auto search_vfs_file = PEONY_SEARCH_VFS_FILE(g_object_new(PEONY_TYPE_SEARCH_VFS_FILE, nullptr)); search_vfs_file->priv->uri = g_strdup(uri); return G_FILE(search_vfs_file); } char *peony_search_vfs_file_get_path(GFile *file) { return nullptr; } void peony_search_vfs_file_enumerator_parse_uri(PeonySearchVFSFileEnumerator *enumerator, const char *uri) { PeonySearchVFSFileEnumeratorPrivate *details = enumerator->priv; *details->search_vfs_directory_uri = uri; auto manager = Peony::SearchVFSManager::getInstance(); if (manager->hasHistory(uri)) { auto uris = manager->getHistroyResults(uri); for (auto uri: uris) { details->enumerate_queue->enqueue(uri); } //do not parse uri, not neccersary return; } QStringList args = details->search_vfs_directory_uri->split("&", QString::SkipEmptyParts); //we should judge case sensitive, then we confirm the regexp when //we match file in file enumeration. for (auto arg: args) { //qDebug()<search_hidden = true; } continue; } if (arg.contains("use_regexp=")) { if (arg.endsWith("0")) { details->use_regexp = false; } continue; } if (arg.contains("case_sensitive=")) { if (arg.endsWith("1")) { details->case_sensitive = false; } continue; } if (arg.contains("name_regexp=")) { if (arg == "name_regexp=") { continue; } QString tmp = arg; tmp = tmp.remove("name_regexp="); details->name_regexp = new QRegExp(tmp); continue; } if (arg.contains("extend_regexp=")) { if (arg == "extend_regexp=") continue; QString tmp = arg; tmp = tmp.remove("extend_regexp="); QStringList keys = tmp.split(",", QString::SkipEmptyParts); for(auto key : keys) { details->name_regexp_extend_list->append(new QRegExp(key)); } continue; } if (arg.contains("content_regexp=")) { if (arg == "content_regexp=") { continue; } QString tmp = arg; tmp = tmp.remove("content_regexp="); details->content_regexp = new QRegExp(tmp); continue; } if (arg.contains("save=")) { if (arg.endsWith("1")) { details->save_result = true; } continue; } if (arg.contains("recursive=")) { if (arg.endsWith("1")) { details->recursive = true; } else details->save_result = false; continue; } if (arg.contains("search_uris=")) { QString tmp = arg; tmp.remove("search:///"); tmp.remove("search_uris="); QStringList uris = tmp.split(",", QString::SkipEmptyParts); for (auto uri: uris) { //NOTE: we should enumerate the search uris and add //the children into queue first. otherwise we could //not judge wether we should search recursively. Peony::FileEnumerator e; e.setEnumerateDirectory(uri); e.enumerateSync(); auto uris1 = e.getChildrenUris(); for (auto uri1 : uris1) { details->enumerate_queue->enqueue(uri1); } } } } Qt::CaseSensitivity sensitivity = details->case_sensitive? Qt::CaseSensitive: Qt::CaseInsensitive; if (!details->name_regexp) { //details->name_regexp = new QRegExp; } else { details->name_regexp->setCaseSensitivity(sensitivity); } if (!details->content_regexp) { //details->content_regexp = new QRegExp; } else { details->content_regexp->setCaseSensitivity(sensitivity); } if (details->name_regexp_extend_list->count() >0) { for(int i=0; iname_regexp_extend_list->count(); i++) { details->name_regexp_extend_list->at(i)->setCaseSensitivity(sensitivity); } } } GFileEnumerator *peony_search_vfs_file_enumerate_children_internal(GFile *file, const char *attributes, GFileQueryInfoFlags flags, GCancellable *cancellable, GError **error) { auto vfs_file = PEONY_SEARCH_VFS_FILE(file); //we should add enumerator container when search vfs enumerator created. //otherwise g_enumerator_get_child will went error. auto enumerator = PEONY_SEARCH_VFS_FILE_ENUMERATOR(g_object_new(PEONY_TYPE_SEARCH_VFS_FILE_ENUMERATOR, "container", file, nullptr)); peony_search_vfs_file_enumerator_parse_uri(enumerator, vfs_file->priv->uri); //parse uri, add top folder uri to queue; return G_FILE_ENUMERATOR(enumerator); } peony/libpeony-qt/vfs/peony-search-vfs-file.h0000644000175000017500000000320514205101223020136 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef PEONYSEARCHVFSFILE_H #define PEONYSEARCHVFSFILE_H #include #include G_BEGIN_DECLS #define PEONY_TYPE_SEARCH_VFS_FILE peony_search_vfs_file_get_type() G_DECLARE_FINAL_TYPE(PeonySearchVFSFile, peony_search_vfs_file, PEONY, SEARCH_VFS_FILE, GObject) PeonySearchVFSFile *peony_search_vfs_file_new(void); typedef struct { gchar *uri; } PeonySearchVFSFilePrivate; struct _PeonySearchVFSFile { GObject parent_instance; PeonySearchVFSFilePrivate *priv; }; G_END_DECLS extern "C" { GFile *peony_search_vfs_file_new_for_uri(const char *uri); static GFileEnumerator *peony_search_vfs_file_enumerate_children_internal(GFile *file, const char *attribute, GFileQueryInfoFlags flags, GCancellable *cancellable, GError **error); } #endif // PEONYSEARCHVFSFILE_H peony/libpeony-qt/vfs/recent-vfs-manager.cpp0000644000175000017500000001270414205101223020053 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: ding jing * */ #include "recent-vfs-manager.h" #include #include #include #include #include using namespace Peony; RecentVFSManager* RecentVFSManager::m_instance = nullptr; RecentVFSManager* RecentVFSManager::getInstance() { if (nullptr == m_instance) { m_instance = new RecentVFSManager; } return m_instance; } void RecentVFSManager::clearAll() { QFile file (m_recent_path); QXmlStreamWriter xmlWritter; xmlWritter.setAutoFormatting(true); file.open(QIODevice::WriteOnly | QIODevice::Text); xmlWritter.setDevice(&file); xmlWritter.writeStartDocument(); xmlWritter.writeStartElement("xbel"); xmlWritter.writeAttribute("version", "1.0"); xmlWritter.writeAttribute("xmlns:bookmark", "http://www.freedesktop.org/standards/desktop-bookmarks"); xmlWritter.writeAttribute("xmlns:mime", "http://www.freedesktop.org/standards/shared-mime-info"); xmlWritter.writeEndElement(); xmlWritter.writeEndDocument(); file.close(); } void RecentVFSManager::insert(QString uri, QString mimetype, QString name, QString exec) { if (!exists(uri)) { createNode(uri, mimetype, name, exec); write(); } } RecentVFSManager::RecentVFSManager(QObject *parent) : QObject(parent) { m_recent_path = QStandardPaths::locate(QStandardPaths::HomeLocation, "/.local/share/recently-used.xbel"); QFile file (m_recent_path); if (!file.exists()) { clearAll(); } } bool RecentVFSManager::read() { QString errorStr; int errorLine; int errorColumn; QFile file (m_recent_path); if (!file.exists()) { return false; } file.open(QIODevice::ReadOnly | QIODevice::Text); if (!m_dom_document.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) { qDebug() << "line:" << errorLine << " column:" << errorColumn << " info:" << errorStr; return false; } QDomElement root = m_dom_document.documentElement(); if ("xbel" != root.tagName()) { qDebug () << "not xbel document"; return false; } else if (root.hasAttribute("version") && "1.0" != root.attribute("version")) { qDebug() << "xbel version is not 1.0"; return false; } return true; } bool RecentVFSManager::write() { QFile file (m_recent_path); file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate); file.write(m_dom_document.toByteArray()); file.flush(); file.close(); return true; } bool RecentVFSManager::exists(QString uri) { if (!read()) { qDebug() << "read error"; return false; } QDomElement rootElement = m_dom_document.firstChildElement().firstChildElement(); while (!rootElement.isNull()) { if (rootElement.hasAttribute("href") && rootElement.attribute("href") == uri) { qDebug() << "existed!"; return true; } rootElement = rootElement.nextSiblingElement(); } return false; } bool RecentVFSManager::createNode(QString uri, QString mimetype, QString name, QString exec) { QString dataTime = QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm:ssZ"); QDomElement rootElement = m_dom_document.firstChildElement(); rootElement.setAttribute("version", "1.0"); rootElement.setAttribute("xmlns:bookmark", "http://www.freedesktop.org/standards/desktop-bookmarks"); rootElement.setAttribute("xmlns:mime", "http://www.freedesktop.org/standards/shared-mime-info"); QDomElement child = m_dom_document.createElement("bookmark"); child.setAttribute("href", uri); child.setAttribute("added", dataTime); child.setAttribute("modified", dataTime); child.setAttribute("visited", dataTime); QDomElement childInfo = m_dom_document.createElement("info"); QDomElement childInfoMeta = m_dom_document.createElement("metadata"); childInfoMeta.setAttribute("owner", "http://freedesktop.org"); QDomElement childInfoMetaMime = m_dom_document.createElement("mime:mime-type"); childInfoMetaMime.setAttribute("type", mimetype); QDomElement childInfoMetaApp = m_dom_document.createElement("bookmark:applications"); // maybe not only one application QDomElement app = m_dom_document.createElement("bookmark:application"); app.setAttribute("name", name); app.setAttribute("exec", QString("'%1 %u'").arg(exec)); app.setAttribute("modified", dataTime); app.setAttribute("count", "1"); childInfoMetaApp.appendChild(app); childInfoMeta.appendChild(childInfoMetaMime); childInfoMeta.appendChild(childInfoMetaApp); childInfo.appendChild(childInfoMeta); child.appendChild(childInfo); rootElement.appendChild(child); return true; } peony/libpeony-qt/vfs/vfs-plugin-manager.cpp0000644000175000017500000000414114205101223020065 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "vfs-plugin-manager.h" #include "favorite-vfs-register.h" #include "search-vfs-register.h" using namespace Peony; static VFSPluginManager *global_instance = nullptr; VFSPluginManager *VFSPluginManager::getInstance() { if (!global_instance) global_instance = new VFSPluginManager; return global_instance; } void VFSPluginManager::registerPlugin(VFSPluginIface *plugin) { if (m_support_schemes.contains(plugin->uriScheme())) return; plugin->initVFS(); m_plugins<uriScheme(); } QList VFSPluginManager::registeredPlugins() { return m_plugins; } GFile *VFSPluginManager::newVFSFile(const QString &uri) { int index = -1; for (auto scheme : m_support_schemes) { if (uri.startsWith(scheme)) { index = m_support_schemes.indexOf(scheme); } } if (index >= 0) { auto plugin = m_plugins.at(index); return G_FILE(plugin->parseUriToVFSFile(uri)); } else { return g_file_new_for_uri(uri.toUtf8().constData()); } } VFSPluginManager::VFSPluginManager(QObject *parent) : QObject(parent) { auto searchVFSPlugin = new SearchVFSInternalPlugin; registerPlugin(searchVFSPlugin); auto favoriteVFSPlugin = new FavoriteVFSInternalPlugin; registerPlugin(favoriteVFSPlugin); } peony/libpeony-qt/vfs/recent-vfs-manager.h0000644000175000017500000000277314205101223017525 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: ding jing * */ #ifndef RECENTVFSMANAGER_H #define RECENTVFSMANAGER_H #include #include #include namespace Peony { class PEONYCORESHARED_EXPORT RecentVFSManager : public QObject { Q_OBJECT public: static RecentVFSManager* getInstance (); void clearAll (); void insert (QString uri, QString mimetype, QString name, QString exec); private: explicit RecentVFSManager(QObject *parent = nullptr); bool read (); bool write (); bool exists (QString uri); bool createNode (QString uri, QString mimetype, QString name, QString exec); private: static RecentVFSManager* m_instance; QString m_recent_path; QDomDocument m_dom_document; }; }; #endif // RECENTVFSMANAGER_H peony/libpeony-qt/vfs/search-vfs-register.cpp0000644000175000017500000000502614205101223020251 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "search-vfs-register.h" #include "peony-search-vfs-file.h" #include "peony-search-vfs-file-enumerator.h" #include "search-vfs-manager.h" #include #include using namespace Peony; bool is_registed = false; static GFile * test_vfs_parse_name (GVfs *vfs, const char *parse_name, gpointer user_data) { QString tmp = parse_name; if (tmp.contains("real-uri:")) { QString realUri = tmp.split("real-uri:").last(); return g_file_new_for_uri(realUri.toUtf8().constData()); } return peony_search_vfs_file_new_for_uri(parse_name); } static GFile * test_vfs_lookup (GVfs *vfs, const char *uri, gpointer user_data) { return test_vfs_parse_name(vfs, uri, user_data); } void SearchVFSRegister::registSearchVFS() { if (is_registed) return; //init manager Peony::SearchVFSManager::getInstance(); GVfs *vfs; const gchar * const *schemes; gboolean res; vfs = g_vfs_get_default (); schemes = g_vfs_get_supported_uri_schemes(vfs); const gchar * const *p; p = schemes; while (*p) { qDebug()<<*p; p++; } #if GLIB_CHECK_VERSION(2, 50, 0) res = g_vfs_register_uri_scheme (vfs, "search", test_vfs_lookup, NULL, NULL, test_vfs_parse_name, NULL, NULL); #else //FIXME: how to implement search operation in old glib? #endif } SearchVFSRegister::SearchVFSRegister() { } void SearchVFSInternalPlugin::initVFS() { SearchVFSRegister::registSearchVFS(); } void *SearchVFSInternalPlugin::parseUriToVFSFile(const QString &uri) { return peony_search_vfs_file_new_for_uri(uri.toUtf8().constData()); } peony/libpeony-qt/vfs/peony-search-vfs-file-enumerator.cpp0000644000175000017500000003316014205115226022663 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "peony-search-vfs-file-enumerator.h" #include "peony-search-vfs-file.h" #include "file-enumerator.h" #include "search-vfs-manager.h" #include #include #include #include //G_DEFINE_TYPE(PeonySearchVFSFileEnumerator, peony_search_vfs_file_enumerator, G_TYPE_FILE_ENUMERATOR) G_DEFINE_TYPE_WITH_PRIVATE(PeonySearchVFSFileEnumerator, peony_search_vfs_file_enumerator, G_TYPE_FILE_ENUMERATOR) static void peony_search_vfs_file_enumerator_parse_uri(PeonySearchVFSFileEnumerator *enumerator, const char *uri); static gboolean peony_search_vfs_file_enumerator_is_file_match(PeonySearchVFSFileEnumerator *enumerator, const QString &uri); static void peony_search_vfs_file_enumerator_add_directory_to_queue(PeonySearchVFSFileEnumerator *enumerator, const QString &directory_uri) { auto queue = enumerator->priv->enumerate_queue; GError *err = nullptr; GFile *top = g_file_new_for_uri(directory_uri.toUtf8().constData()); GFileEnumerator *e = g_file_enumerate_children(top, G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, &err); if (err) { QString errMsg = err->message; g_error_free(err); } g_object_unref(top); if (!e) return; auto child_info = g_file_enumerator_next_file(e, nullptr, nullptr); while (child_info) { auto child = g_file_enumerator_get_child(e, child_info); auto uri = g_file_get_uri(child); *queue<priv = priv; self->priv->search_vfs_directory_uri = new QString; self->priv->enumerate_queue = new QQueue; self->priv->name_regexp_extend_list = new QList; self->priv->recursive = false; self->priv->save_result = false; self->priv->search_hidden = true; self->priv->use_regexp = true; self->priv->case_sensitive = false; self->priv->match_name_or_content = true; } static void enumerator_dispose(GObject *object); static GFileInfo *enumerate_next_file(GFileEnumerator *enumerator, GCancellable *cancellable, GError **error); /// async method is modified from glib source file gfileenumerator.c static void enumerate_next_files_async (GFileEnumerator *enumerator, int num_files, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); static GList* //GFileInfo* enumerate_next_files_finished(GFileEnumerator *enumerator, GAsyncResult *result, GError **error); static gboolean enumerator_close(GFileEnumerator *enumerator, GCancellable *cancellable, GError **error); static void peony_search_vfs_file_enumerator_class_init (PeonySearchVFSFileEnumeratorClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS(klass); gobject_class->dispose = enumerator_dispose; enumerator_class->next_file = enumerate_next_file; //async enumerator_class->next_files_async = enumerate_next_files_async; enumerator_class->next_files_finish = enumerate_next_files_finished; enumerator_class->close_fn = enumerator_close; } void enumerator_dispose(GObject *object) { PeonySearchVFSFileEnumerator *self = PEONY_SEARCH_VFS_FILE_ENUMERATOR(object); if (self->priv->name_regexp) delete self->priv->name_regexp; if (self->priv->content_regexp) delete self->priv->content_regexp; delete self->priv->search_vfs_directory_uri; self->priv->enumerate_queue->clear(); delete self->priv->enumerate_queue; for(int i=self->priv->name_regexp_extend_list->count()-1; i>=0; i--) { delete self->priv->name_regexp_extend_list->at(i); } delete self->priv->name_regexp_extend_list; } static GFileInfo *enumerate_next_file(GFileEnumerator *enumerator, GCancellable *cancellable, GError **error) { auto manager = Peony::SearchVFSManager::getInstance(); qDebug()<<"next file"; if (cancellable) { if (g_cancellable_is_cancelled(cancellable)) { //FIXME: how to add translation here? do i have to use gettext? *error = g_error_new_literal(G_IO_ERROR, G_IO_ERROR_CANCELLED, "search is cancelled"); return nullptr; } } auto search_enumerator = PEONY_SEARCH_VFS_FILE_ENUMERATOR(enumerator); auto enumerate_queue = search_enumerator->priv->enumerate_queue; if (manager->hasHistory(*search_enumerator->priv->search_vfs_directory_uri)) { while (!enumerate_queue->isEmpty()) { auto uri = enumerate_queue->dequeue(); auto search_vfs_info = g_file_info_new(); QString realUriSuffix = "real-uri:" + uri; g_file_info_set_name(search_vfs_info, realUriSuffix.toUtf8().constData()); return search_vfs_info; } return nullptr; } while (!enumerate_queue->isEmpty()) { //BFS enumeration auto uri = enumerate_queue->dequeue(); GFile *tmp = g_file_new_for_uri(uri.toUtf8().constData()); GFileType type = g_file_query_file_type(tmp, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr); g_object_unref(tmp); bool isDir = type == G_FILE_TYPE_DIRECTORY; if (isDir && search_enumerator->priv->recursive) { if (!search_enumerator->priv->search_hidden) { if (!uri.contains("/.")) { peony_search_vfs_file_enumerator_add_directory_to_queue(search_enumerator, uri); } } else { peony_search_vfs_file_enumerator_add_directory_to_queue(search_enumerator, uri); } } //match if (peony_search_vfs_file_enumerator_is_file_match(search_enumerator, uri)) { //return this info, and the enumerate get child will return the //file crosponding the real uri, due to it would be handled in //vfs looking up method callback in registed vfs. if (!search_enumerator->priv->search_hidden) { if (uri.contains("/.")) { goto return_info; } } else { return_info: 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); //FIXME: add lock? historyResults<next_file (enumerator, cancellable, &error); if (info == NULL) { break; } else files = g_list_prepend (files, info); } if (error) g_task_return_error (task, error); else g_task_return_pointer (task, files, (GDestroyNotify)next_async_op_free); } static void enumerate_next_files_async (GFileEnumerator *enumerator, int num_files, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (enumerator, cancellable, callback, user_data); g_task_set_source_tag (task, (gpointer)enumerate_next_files_async); g_task_set_task_data (task, GINT_TO_POINTER (num_files), NULL); g_task_set_priority (task, io_priority); g_task_run_in_thread (task, next_files_thread); g_object_unref (task); } static GList* //GFileInfo* enumerate_next_files_finished(GFileEnumerator *enumerator, GAsyncResult *result, GError **error) { g_return_val_if_fail (g_task_is_valid (result, enumerator), NULL); return (GList*)g_task_propagate_pointer (G_TASK (result), error); } static gboolean enumerator_close(GFileEnumerator *enumerator, GCancellable *cancellable, GError **error) { PeonySearchVFSFileEnumerator *self = PEONY_SEARCH_VFS_FILE_ENUMERATOR(enumerator); return true; } gboolean peony_search_vfs_file_enumerator_is_file_match(PeonySearchVFSFileEnumerator *enumerator, const QString &uri) { PeonySearchVFSFileEnumeratorPrivate *details = enumerator->priv; if (!details->name_regexp && !details->content_regexp && details->name_regexp_extend_list->count() == 0) return false; GFile *file = g_file_new_for_uri(uri.toUtf8().constData()); GFileInfo *info = g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); g_object_unref(file); char *file_display_name = g_file_info_get_attribute_as_string(info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME); g_object_unref(info); QString displayName = file_display_name; g_free(file_display_name); if (details->name_regexp) { if (details->use_regexp && details->match_name_or_content && displayName.contains(*enumerator->priv->name_regexp)) { return true; } else if (displayName == details->name_regexp->pattern()) { //this is most used for querying files which might be duplicate. return true; } } //extend list name match if (details->name_regexp_extend_list->count() >0) { for(int i=0; iname_regexp_extend_list->count(); i++) { auto curRegexp = details->name_regexp_extend_list->at(i); //qDebug() <<"curRegexp:" <<*curRegexp<use_regexp && details->match_name_or_content && displayName.contains(*curRegexp)) { return true; } else if (displayName == curRegexp->pattern()) { //this is most used for querying files which might be duplicate. return true; } } } if (details->content_regexp) { //read file stream QUrl url = uri; QFile file(url.path()); file.open(QIODevice::Text | QIODevice::ReadOnly); QTextStream stream(&file); bool content_matched = false; QString line; line = stream.readLine(); while (!line.isNull()) { if (line.contains(*enumerator->priv->content_regexp)) { content_matched = true; break; } line = stream.readLine(); } file.close(); if (content_matched) { if (enumerator->priv->match_name_or_content) { return true; } } } //this may never happend. return false; } peony/libpeony-qt/vfs/search-vfs-manager.h0000644000175000017500000000324514205101223017505 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef SEARCHVFSMANAGER_H #define SEARCHVFSMANAGER_H #include #include #include namespace Peony { class SearchVFSManager : public QObject { Q_OBJECT public: static SearchVFSManager *getInstance(); public Q_SLOTS: void clearHistory(); /*! * \brief clearHistoryOne * \param searchUri * \details * if we refresh the directory, we should clean the history of the * directory and search again. */ void clearHistoryOne(const QString &searchUri); void addHistory(const QString &searchUri, const QStringList &results); bool hasHistory(const QString &serachUri); QStringList getHistroyResults(const QString &searchUri) ; private: explicit SearchVFSManager(QObject *parent = nullptr); ~SearchVFSManager(); QMutex m_mutex; QHash m_search_dir_results_hash; }; } #endif // SEARCHVFSMANAGER_H peony/libpeony-qt/vfs/favorite-vfs-file-monitor.cpp0000644000175000017500000003415214205101223021405 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Ding Jing * */ #include "favorite-vfs-file-monitor.h" #include #include #include #include enum { VFS_FAVORITE_FILE_MONITOR_CHANGE, VFS_FAVORITE_FILE_MONITOR_LAST }; struct _QueuedEvent { GFileMonitorEvent eventType; GFile* child; GFile* other; }; struct _PendingChange { gchar* child; guint64 lastEmission : 63; guint64 dirty : 1; }; struct _FavoriteVFSFileMonitorSource { GSource source; GMutex lock; GWeakRef instanceRef; GFileMonitorFlags flags; gchar* dirname; gchar* basename; gchar* filename; GSequence* pendingChanges; GHashTable* pendingChangesTable; GQueue eventQueue; gint64 rateLimit; }; struct _FavoriteVFSFileMonitorPrivate { GList* fileList; }; typedef struct _QueuedEvent QueuedEvent; typedef struct _PendingChange PendingChange; #define DEFAULT_RATE_LIMIT 800 * G_TIME_SPAN_MILLISECOND #define VIRTUAL_CHANGES_DONE_DELAY 2 * G_TIME_SPAN_SECOND G_DEFINE_TYPE (FavoriteVFSFileMonitor, vfs_favorite_file_monitor, G_TYPE_FILE_MONITOR) static guint str_hash0 (gconstpointer str); static void pending_change_free (gpointer data); static void queued_event_free (QueuedEvent *event); static gboolean str_equal0 (gconstpointer a, gconstpointer b); static void vfs_favorite_file_monitor_dispose (GObject* obj); static void vfs_favorite_file_monitor_finalize (GObject* obj); static void vfs_favorite_file_monitor_source_finalize (GSource *source); static void vfs_favorite_file_monitor_g_file_monitor_init (GFileMonitor* iface); static gint64 g_file_monitor_source_get_ready_time (FavoriteVFSFileMonitorSource* fms); static void g_file_monitor_source_update_ready_time (FavoriteVFSFileMonitorSource* fms); static int pending_change_compare_ready_time (gconstpointer a_p, gconstpointer b_p, gpointer udata); static gint64 pending_change_get_ready_time (const PendingChange* change, FavoriteVFSFileMonitorSource* fms); static gboolean vfs_favorite_file_monitor_source_dispatch (GSource* source, GSourceFunc callback, gpointer udata); static void g_file_monitor_source_remove_pending_change (FavoriteVFSFileMonitorSource* fms, GSequenceIter* iter, const gchar* child); FavoriteVFSFileMonitor* vfs_favorite_file_monitor_new_for_path(const char *path, gboolean isDirectory, GFileMonitorFlags flags, GError **); static void vfs_favorite_file_monitor_source_queue_event (FavoriteVFSFileMonitorSource* fms, GFileMonitorEvent event_type, const gchar* child, GFile* other); static void vfs_favorite_file_monitor_start (FavoriteVFSFileMonitor* monitor, const gchar* filename, gboolean isDir, GFileMonitorFlags flags, GMainContext* ctx); static guint vfs_favorite_file_monitor_signals [VFS_FAVORITE_FILE_MONITOR_LAST] = {0}; static void vfs_favorite_file_monitor_init (FavoriteVFSFileMonitor* self) { g_return_if_fail(VFS_IS_FAVORITE_FILE_MONITOR(self)); self->fileList = nullptr; } static void vfs_favorite_file_monitor_class_init (FavoriteVFSFileMonitorClass* self) { GObjectClass* selfClass = G_OBJECT_CLASS (self); selfClass->dispose = vfs_favorite_file_monitor_dispose; selfClass->finalize = vfs_favorite_file_monitor_finalize; } static void vfs_favorite_file_monitor_dispose (GObject* obj) { g_return_if_fail(VFS_IS_FAVORITE_FILE_MONITOR(obj)); FavoriteVFSFileMonitor* self = VFS_FAVORITE_FILE_MONITOR(obj); if (nullptr != self->fileList) { g_list_free_full(self->fileList, g_object_unref); self->fileList = nullptr; } QObject::disconnect(self->add); QObject::disconnect(self->remove); } static void vfs_favorite_file_monitor_finalize (GObject* obj) { g_return_if_fail(VFS_IS_FAVORITE_FILE_MONITOR(obj)); G_OBJECT_CLASS (vfs_favorite_file_monitor_parent_class)->finalize (obj); } static FavoriteVFSFileMonitor* vfs_favorite_file_monitor_new () { return VFS_FAVORITE_FILE_MONITOR(g_object_new (VFS_TYPE_FAVORITE_FILE_MONITOR, nullptr)); } gboolean vfs_favorite_file_monitor_source_handle_event(FavoriteVFSFileMonitorSource *, GFileMonitorEvent, const gchar *child, const gchar *renameTo, GFile *other, gint64 eTime) { return FALSE; } FavoriteVFSFileMonitor* vfs_favorite_file_monitor_new_for_path(const char *path, gboolean isDirectory, GFileMonitorFlags flags, GError** error) { FavoriteVFSFileMonitor* monitor = nullptr; monitor = vfs_favorite_file_monitor_new (); if (monitor) { vfs_favorite_file_monitor_start (monitor, path, isDirectory, flags, (GMainContext*)*error); } return monitor; } static void vfs_favorite_file_monitor_start (FavoriteVFSFileMonitor* monitor, const gchar* filename, gboolean isDir, GFileMonitorFlags flags, GMainContext* ctx) { g_return_if_fail(VFS_IS_FAVORITE_FILE_MONITOR(monitor)); FavoriteVFSFileMonitorClass* classs = VFS_FAVORITE_FILE_MONITOR_GET_CLASS (monitor); FavoriteVFSFileMonitorSource* source = nullptr; g_assert (!monitor->source); Peony::BookMarkManager* gm = Peony::BookMarkManager::getInstance(); gm->connect(gm, &Peony::BookMarkManager::bookMarkAdded, [=] (const QString &uri, bool successed) { if (successed) { GFile* file = g_file_new_for_uri(uri.toUtf8().constData()); g_file_monitor_emit_event(&(monitor->parent_instance), file, nullptr, G_FILE_MONITOR_EVENT_CREATED); } }); } static FavoriteVFSFileMonitorSource* vfs_favorite_file_monitor_source_new (gpointer instance, const gchar* filename, GFileMonitorFlags flags) { FavoriteVFSFileMonitorSource* fms = nullptr; GSource* source = nullptr; static GSourceFuncs sourceFuncs = {nullptr, nullptr, vfs_favorite_file_monitor_source_dispatch, vfs_favorite_file_monitor_source_finalize}; source = g_source_new (&sourceFuncs, sizeof (FavoriteVFSFileMonitorSource)); fms = (FavoriteVFSFileMonitorSource*) source; g_source_set_name (source, "FavoriteVFSFileMonitorSource"); g_mutex_init (&fms->lock); g_weak_ref_init (&fms->instanceRef, instance); fms->pendingChanges = g_sequence_new (pending_change_free); fms->pendingChangesTable = g_hash_table_new (str_hash0, str_equal0); fms->rateLimit = DEFAULT_RATE_LIMIT; fms->flags = flags; if (flags & G_FILE_MONITOR_WATCH_HARD_LINKS) { fms->dirname = NULL; fms->basename = NULL; fms->filename = g_strdup (filename); } else { fms->dirname = g_path_get_dirname (filename); fms->basename = g_path_get_basename (filename); fms->filename = NULL; } return fms; } static gboolean vfs_favorite_file_monitor_source_dispatch (GSource* source, GSourceFunc callback, gpointer udata) { gint64 now = 0; GQueue eventQueue; QueuedEvent* event = nullptr; GFileMonitor* instance = nullptr; FavoriteVFSFileMonitorSource* vms = (FavoriteVFSFileMonitorSource*) source; instance = (GFileMonitor*) g_weak_ref_get (&vms->instanceRef); if (nullptr == instance) { return FALSE; } now = g_source_get_time (source); g_mutex_lock (&vms->lock); while (!g_sequence_is_empty (vms->pendingChanges)) { GSequenceIter* iter = g_sequence_get_begin_iter (vms->pendingChanges); PendingChange* pending = (PendingChange*) g_sequence_get (iter); if (pending_change_get_ready_time (pending, vms) > now) { break; } if (pending->dirty) { vfs_favorite_file_monitor_source_queue_event (vms, G_FILE_MONITOR_EVENT_CHANGED, pending->child, NULL); pending->lastEmission = now; pending->dirty = FALSE; g_sequence_sort_changed (iter, pending_change_compare_ready_time, vms); } else { vfs_favorite_file_monitor_source_queue_event (vms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, pending->child, NULL); g_file_monitor_source_remove_pending_change (vms, iter, pending->child); } } memcpy (&eventQueue, &vms->eventQueue, sizeof eventQueue); memset (&vms->eventQueue, 0, sizeof vms->eventQueue); g_file_monitor_source_update_ready_time (vms); g_clear_object (&instance); g_mutex_unlock (&vms->lock); while ((event = (QueuedEvent*) g_queue_pop_head (&eventQueue))) { instance = (GFileMonitor*) g_weak_ref_get (&vms->instanceRef); if (instance != NULL) { g_file_monitor_emit_event (instance, event->child, event->other, event->eventType); } g_clear_object (&instance); queued_event_free (event); } return TRUE; } static void vfs_favorite_file_monitor_source_finalize (GSource *source) { FavoriteVFSFileMonitorSource* fms = (FavoriteVFSFileMonitorSource*) source; g_assert (nullptr == g_weak_ref_get (&fms->instanceRef)); g_weak_ref_clear (&fms->instanceRef); g_assert (g_sequence_is_empty (fms->pendingChanges)); g_assert (g_hash_table_size (fms->pendingChangesTable) == 0); g_assert (fms->eventQueue.length == 0); g_hash_table_unref (fms->pendingChangesTable); g_sequence_free (fms->pendingChanges); g_free (fms->dirname); g_free (fms->basename); g_free (fms->filename); g_mutex_clear (&fms->lock); } static gint64 pending_change_get_ready_time (const PendingChange* change, FavoriteVFSFileMonitorSource* fms) { if (change->dirty) { return change->lastEmission + fms->rateLimit; } else { return change->lastEmission + VIRTUAL_CHANGES_DONE_DELAY; } } static void vfs_favorite_file_monitor_source_queue_event (FavoriteVFSFileMonitorSource* fms, GFileMonitorEvent event_type, const gchar* child, GFile* other) { QueuedEvent *event; event = g_slice_new (QueuedEvent); event->eventType = event_type; event->other = other; if (other) { g_object_ref (other); } g_queue_push_tail (&fms->eventQueue, event); } static guint str_hash0 (gconstpointer str) { return str ? g_str_hash (str) : 0; } static gboolean str_equal0 (gconstpointer a, gconstpointer b) { return g_strcmp0 ((const char*)a, (const char*)b) == 0; } static void pending_change_free (gpointer data) { PendingChange* change = (PendingChange*)data; g_free (change->child); g_slice_free (PendingChange, change); } static int pending_change_compare_ready_time (gconstpointer a_p, gconstpointer b_p, gpointer udata) { FavoriteVFSFileMonitorSource *fms = (FavoriteVFSFileMonitorSource*)udata; const PendingChange *a = (PendingChange*)a_p; const PendingChange *b = (PendingChange*)b_p; gint64 ready_time_a; gint64 ready_time_b; ready_time_a = pending_change_get_ready_time (a, fms); ready_time_b = pending_change_get_ready_time (b, fms); if (ready_time_a < ready_time_b) { return -1; } else { return ready_time_a > ready_time_b; } } static void g_file_monitor_source_remove_pending_change (FavoriteVFSFileMonitorSource* fms, GSequenceIter* iter, const gchar* child) { g_hash_table_remove (fms->pendingChangesTable, child); g_sequence_remove (iter); } static void g_file_monitor_source_update_ready_time (FavoriteVFSFileMonitorSource* fms) { g_source_set_ready_time ((GSource*) fms, g_file_monitor_source_get_ready_time (fms)); } static gint64 g_file_monitor_source_get_ready_time (FavoriteVFSFileMonitorSource* fms) { GSequenceIter *iter; if (fms->eventQueue.length) { return 0; } iter = g_sequence_get_begin_iter (fms->pendingChanges); if (g_sequence_iter_is_end (iter)) { return -1; } return pending_change_get_ready_time ((const PendingChange*)g_sequence_get (iter), fms); } static void queued_event_free (QueuedEvent *event) { if (event->child) { g_object_unref (event->child); } if (event->other) { g_object_unref (event->other); } g_slice_free (QueuedEvent, event); } void vfs_favorite_file_monitor_free_gfile(FavoriteVFSFileMonitor* obj, GFile* file) { FavoriteVFSFileMonitor* self = VFS_FAVORITE_FILE_MONITOR(obj); if (nullptr != file && G_IS_FILE(file)) { self->fileList = g_list_append(self->fileList, file); } } void vfs_favorite_file_monitor_dir(FavoriteVFSFileMonitor *obj) { g_return_if_fail(VFS_IS_FAVORITE_FILE_MONITOR(obj)); Peony::BookMarkManager* gm = Peony::BookMarkManager::getInstance(); obj->add = QObject::connect(gm, &Peony::BookMarkManager::bookMarkAdded, [=] (const QString &uri, bool successed) { if (successed) { GFile* file = g_file_new_for_uri(uri.toUtf8().constData()); g_file_monitor_emit_event(G_FILE_MONITOR(obj), file, nullptr, G_FILE_MONITOR_EVENT_CREATED); vfs_favorite_file_monitor_free_gfile (VFS_FAVORITE_FILE_MONITOR(obj), G_FILE(file)); } }); obj->remove = QObject::connect(gm, &Peony::BookMarkManager::bookMarkRemoved, [=] (const QString &uri, bool successed) { if (successed) { GFile* file = g_file_new_for_uri(uri.toUtf8().constData()); g_file_monitor_emit_event(G_FILE_MONITOR(obj), file, nullptr, G_FILE_MONITOR_EVENT_DELETED); vfs_favorite_file_monitor_free_gfile (VFS_FAVORITE_FILE_MONITOR(obj), G_FILE(file)); } }); } peony/libpeony-qt/vfs/favorite-vfs-file-enumerator.cpp0000644000175000017500000001710714205115226022110 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Ding Jing * */ #include "favorite-vfs-file-enumerator.h" #include "favorite-vfs-file.h" #include "bookmark-manager.h" #include #include #include extern bool kydroidInstall; extern QString kydroidPath; G_DEFINE_TYPE_WITH_PRIVATE(FavoritesVFSFileEnumerator, vfs_favorites_file_enumerator, G_TYPE_FILE_ENUMERATOR); static void next_async_op_free (GList *files); void vfs_favorites_file_enumerator_dispose (GObject *object); static gboolean enumerator_close (GFileEnumerator *enumerator, GCancellable *cancellable, GError **error); static GFileInfo *enumerate_next_file (GFileEnumerator *enumerator, GCancellable *cancellable, GError **error); static GList* vfs_favorites_file_enumerator_next_files_finished (GFileEnumerator* enumerator, GAsyncResult* result, GError** error); static void next_files_thread (GTask* task, gpointer source_object, gpointer task_data, GCancellable *cancellable); static void vfs_favorites_file_enumerator_next_files_async (GFileEnumerator* enumerator, int num_files, int io_priority, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer user_data); static void vfs_favorites_file_enumerator_init (FavoritesVFSFileEnumerator* self) { g_return_if_fail(VFS_IS_FAVORITES_FILE_ENUMERATOR(self)); FavoritesVFSFileEnumeratorPrivate* priv = (FavoritesVFSFileEnumeratorPrivate*) vfs_favorites_file_enumerator_get_instance_private(self); self->priv = priv; self->priv->enumerate_queue = new QQueue; self->priv->enumerate_queue->enqueue(QString("favorite://?schema=trash")); self->priv->enumerate_queue->enqueue(QString("favorite://%1?schema=file").arg(QStandardPaths::writableLocation(QStandardPaths::MusicLocation))); self->priv->enumerate_queue->enqueue(QString("favorite://%1?schema=file").arg(QStandardPaths::writableLocation(QStandardPaths::MoviesLocation))); self->priv->enumerate_queue->enqueue(QString("favorite://%1?schema=file").arg(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation))); self->priv->enumerate_queue->enqueue(QString("favorite://%1?schema=file").arg(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation))); 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))); if (Peony::FileUtils::isFileExsit("file:///data/usershare")) { self->priv->enumerate_queue->enqueue("favorite:///data/usershare?schema=file"); } // check kydroid is install if (kydroidInstall) { if (kydroidPath.startsWith("kydroid:///")) self->priv->enumerate_queue->enqueue("favorite:///?schema=kydroid"); else self->priv->enumerate_queue->enqueue("favorite:///?schema=kmre"); } // add others auto alluris = Peony::BookMarkManager::getInstance()->getCurrentUris(); for (int i = 0; i < alluris.size(); ++i) { if (vfs_favorite_file_is_exist(alluris.at(i).toUtf8())) { self->priv->enumerate_queue->enqueue(alluris.at(i)); } } } static void vfs_favorites_file_enumerator_class_init (FavoritesVFSFileEnumeratorClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS(klass); GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS(klass); gobject_class->dispose = vfs_favorites_file_enumerator_dispose; enumerator_class->next_file = enumerate_next_file; enumerator_class->close_fn = enumerator_close; enumerator_class->next_files_async = vfs_favorites_file_enumerator_next_files_async; enumerator_class->next_files_finish = vfs_favorites_file_enumerator_next_files_finished; } void vfs_favorites_file_enumerator_dispose(GObject *object) { g_return_if_fail(VFS_IS_FAVORITES_FILE_ENUMERATOR(object)); FavoritesVFSFileEnumerator *self = VFS_FAVORITES_FILE_ENUMERATOR(object); } static GFileInfo *enumerate_next_file (GFileEnumerator *enumerator, GCancellable *cancellable, GError **error) { g_return_val_if_fail(VFS_IS_FAVORITES_FILE_ENUMERATOR(enumerator), nullptr); if (cancellable && g_cancellable_is_cancelled(cancellable)) { *error = g_error_new_literal(G_IO_ERROR, G_IO_ERROR_CANCELLED, "cancelled"); return nullptr; } GFileInfo* fileInfo = nullptr; auto ve = VFS_FAVORITES_FILE_ENUMERATOR(enumerator); auto eq = ve->priv->enumerate_queue; if (!eq->isEmpty()) { GFile* file = g_file_new_for_uri(eq->dequeue().toUtf8()); if (nullptr != file) { fileInfo = g_file_query_info(file, "*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); g_object_unref(file); } } return fileInfo; } static gboolean enumerator_close(GFileEnumerator *enumerator, GCancellable *cancellable, GError **error) { FavoritesVFSFileEnumerator *self = VFS_FAVORITES_FILE_ENUMERATOR(enumerator); return true; } static void vfs_favorites_file_enumerator_next_files_async (GFileEnumerator* enumerator, int num_files, int io_priority, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask* task = g_task_new (enumerator, cancellable, callback, user_data); g_task_set_source_tag (task, (gpointer) vfs_favorites_file_enumerator_next_files_async); g_task_set_task_data (task, GINT_TO_POINTER (num_files), NULL); g_task_set_priority (task, io_priority); g_task_run_in_thread (task, next_files_thread); if (task) { g_object_unref (task); } } static GList* vfs_favorites_file_enumerator_next_files_finished(GFileEnumerator* enumerator, GAsyncResult* result, GError** error) { g_return_val_if_fail (g_task_is_valid (result, enumerator), NULL); return (GList*)g_task_propagate_pointer (G_TASK (result), error); } static void next_files_thread (GTask* task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { auto enumerator = G_FILE_ENUMERATOR(source_object); int num_files = GPOINTER_TO_INT (task_data); int i = 0; GList *files = nullptr; GError *error = nullptr; GFileInfo *info = nullptr; GFileEnumeratorClass* c = G_FILE_ENUMERATOR_GET_CLASS (enumerator); for (i = 0; i < num_files; ++i) { if (g_cancellable_set_error_if_cancelled (cancellable, &error)) { info = NULL; } else { info = c->next_file (enumerator, cancellable, &error); } if (nullptr == info) { break; } else { files = g_list_prepend (files, info); } } if (error) { g_task_return_error (task, error); } else { g_task_return_pointer (task, files, (GDestroyNotify) next_async_op_free); } } static void next_async_op_free (GList *files) { if (files) { g_list_free_full (files, g_object_unref); } } peony/libpeony-qt/vfs/README.md0000644000175000017500000000372614205101223015146 0ustar fengfeng# Peony Qt VFS' implemention ## Framework Peony Qt's framework is based on gvfs, which has been integrated in glib2.0. In gvfs design, user can register it own virtual filesystem in application with unique schema. The theory of custom vfs is based on interface framework of gvfs. If you have understand how Peony::FileEnumerator works, you will know that peony don't care if the file is virtrual, because it uses same method which provided by GFileIface. ## Make a custom vfs Just like the default search vfs which in this directory does. To make a custom vfs in your application, you should: - use g_vfs_register_uri_scheme() register a unique schema - implement your virtual file class with GFileIface as interface g_vfs_register_uri_scheme() is the entry of a custom vfs. It will help us find or create the correct handler when getting a GFile instance by uri. The implement is very flexiable, you might not have to implement all interface of GFileIface unless you have to use one or more method ineeded. Default search vfs shows us how to make a custom search vfs and combine the vfs with Peony::FileEnumerator. The important point is make sure the vfs file handler use its custom enumerator which can parse the search uri and handle it correctly. There are 3 method must be overrided in vfs enumerator which derived from GFileEnumerator: - next_file - next_files_async - next_files_finish ## Improve the search vfs in peony's rules The defaul search vfs provided by peony is low efficiency and waste resources. I hope somebody could provide a vfs that obay the uri parsing rules to replace the original one. The rule of parsing searching uri is: - use "&" as seperator - use "keyword"="varient" to descrip the preference. - use "," to split varient if varient is splitable for the keyword. - aviliable keywords: - name_regexp -- varient type string - content_regexp -- type string - use_regexp -- 0 or 1 - recursive -- 0 or 1 - search_uris -- not null, splitable, type stringpeony/libpeony-qt/vfs/search-vfs-register.h0000644000175000017500000000345114205101223017716 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef SEARCHVFSREGISTER_H #define SEARCHVFSREGISTER_H #include "peony-core_global.h" #include "vfs-plugin-iface.h" namespace Peony { class SearchVFSInternalPlugin : public VFSPluginIface { public: SearchVFSInternalPlugin() {} virtual PluginType pluginType() override {return VFSPlugin;} virtual const QString name() override {return "search vfs";} virtual const QString description() override {return QObject::tr("Default search vfs of peony");} virtual const QIcon icon() override {return QIcon();} virtual void setEnable(bool enable) {} virtual bool isEnable() {return true;} void initVFS() override; QString uriScheme() override {return "search://";} bool holdInSideBar() override {return false;} void *parseUriToVFSFile(const QString &uri) override; CustomErrorHandler *customErrorHandler() override {return nullptr;} }; class PEONYCORESHARED_EXPORT SearchVFSRegister { public: static void registSearchVFS(); private: SearchVFSRegister(); }; } #endif // SEARCHVFSREGISTER_H peony/libpeony-qt/connect-server-dialog.cpp0000644000175000017500000000623714205101223017767 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "connect-server-dialog.h" #include "ui_connect-server-dialog.h" #include "global-settings.h" #include ConnectServerDialog::ConnectServerDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ConnectServerDialog) { ui->setupUi(this); ui->passwd_edit->setEchoMode(QLineEdit::Password); ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Ok")); ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel")); connect(ui->anonymous_checkbox, &QCheckBox::toggled, [=](bool checked) { if (checked) { this->ui->usr_edit->clear(); this->ui->usr_edit->setEnabled(false); this->ui->passwd_edit->clear(); this->ui->passwd_edit->setEnabled(false); if (COMMERCIAL_VERSION) { this->ui->domain_edit->clear(); this->ui->domain_edit->setEnabled(false); } this->ui->save_passwd_checkbox->setChecked(false); this->ui->save_passwd_checkbox->setEnabled(true); } else { this->ui->usr_edit->clear(); this->ui->usr_edit->setEnabled(true); this->ui->passwd_edit->clear(); this->ui->passwd_edit->setEnabled(true); if (COMMERCIAL_VERSION) { this->ui->domain_edit->clear(); this->ui->domain_edit->setEnabled(true); } this->ui->save_passwd_checkbox->setChecked(false); this->ui->save_passwd_checkbox->setEnabled(true); } }); ui->passwd_edit->setEnabled(false); ui->anonymous_checkbox->setChecked(true); if (COMMERCIAL_VERSION) { this->ui->domain_edit->setEnabled(false); this->ui->domain_edit->setVisible(false); this->ui->label_3->setVisible(false); ui->passwd_edit->setEnabled(true); ui->anonymous_checkbox->setChecked(false); } } ConnectServerDialog::~ConnectServerDialog() { disconnect(); delete ui; } QString ConnectServerDialog::user() { return ui->usr_edit->text(); } QString ConnectServerDialog::password() { return ui->passwd_edit->text(); } QString ConnectServerDialog::domain() { return ui->domain_edit->text(); } bool ConnectServerDialog::savePassword() { return ui->save_passwd_checkbox->isChecked(); } bool ConnectServerDialog::anonymous() { return ui->anonymous_checkbox->isChecked(); } peony/libpeony-qt/sync-thread.h0000644000175000017500000000252614205115226015470 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2021, 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 . * * Authors: yanhuanzhang * */ #ifndef SYNCTHREAD_H #define SYNCTHREAD_H #include #include #include "peony-core_global.h" #include namespace Peony{ class PEONYCORESHARED_EXPORT SyncThread : public QObject { Q_OBJECT public: explicit SyncThread(QString uri, QObject *parent = nullptr); static void notifyUser(QString notifyContent); Q_SIGNALS: void syncFinished(); public Q_SLOTS: void parentStartedSlot(); private: QString mHint; QString mUri; }; } #endif // SYNCTHREAD_H peony/libpeony-qt/file-enumerator.cpp0000644000175000017500000005607414205115226016707 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-enumerator.h" #include "file-info.h" #include "file-info-manager.h" #include "file-info-job.h" #include "mount-operation.h" #include "gerror-wrapper.h" #include "file-utils.h" #include "audio-play-manager.h" #include "vfs-plugin-manager.h" #include "custom-error-handler.h" #include #include #include #include #include #ifndef PEONY_FIND_NEXT_FILES_BATCH_SIZE #define PEONY_FIND_NEXT_FILES_BATCH_SIZE 100 #endif using namespace Peony; FileEnumerator::FileEnumerator(QObject *parent) : QObject(parent) { m_root_file = g_file_new_for_uri("file:///"); m_cancellable = g_cancellable_new(); m_children_uris = new QList(); m_cache_uris = new QStringList(); m_idle = new QTimer(this); m_idle->setSingleShot(false); connect(this, &FileEnumerator::enumerateFinished, this, [=]() { if (m_auto_delete) { this->deleteLater(); } }); connect(this, &FileEnumerator::enumerateFinished, this, [=](bool successed){ if (successed) { *m_children_uris<<*m_cache_uris; childrenUpdated(*m_cache_uris, true); } m_cache_uris->clear(); m_idle->stop(); }); connect(m_idle, &QTimer::timeout, this, [=](){ *m_children_uris<<*m_cache_uris; childrenUpdated(*m_cache_uris); m_cache_uris->clear(); }); } /*! * \brief FileEnumerator::~FileEnumerator * \note * When a file enumerator instance in deconstructor, * we need consider if we need free the data of info. * If only global hash hold the shared data, it should. *
* It's just an insurance measure, we usually manage children list in other classes. * So, usually there's no data being released here. * You can notice that the use count of info should be free is 3. because temporary list, * list element and global hash will all hold one shared_ptr here. * \see FileInfo::~FileInfo() *
*/ FileEnumerator::~FileEnumerator() { g_cancellable_cancel(m_cancellable); disconnect(); //qDebug()<<"~FileEnumerator"; g_object_unref(m_root_file); g_object_unref(m_cancellable); delete m_children_uris; delete m_cache_uris; } void FileEnumerator::setEnumerateDirectory(QString uri) { m_uri = uri; if (m_cancellable) { g_cancellable_cancel(m_cancellable); g_object_unref(m_cancellable); } m_cancellable = g_cancellable_new(); if (m_root_file) { g_object_unref(m_root_file); } // TODO: implement a check for vfs extensions. //handle search can not use before glib 2.50 #if GLIB_CHECK_VERSION(2, 50, 0) m_root_file = g_file_new_for_uri(uri.toUtf8()); #else auto vfsMgr = VFSPluginManager::getInstance(); m_root_file = vfsMgr->newVFSFile(uri); #endif } void FileEnumerator::setEnumerateDirectory(GFile *file) { if (m_cancellable) { g_cancellable_cancel(m_cancellable); g_object_unref(m_cancellable); } m_cancellable = g_cancellable_new(); if (m_root_file) { g_object_unref(m_root_file); } m_root_file = g_file_dup(file); char *uri = g_file_get_uri(m_root_file); if (uri) { m_uri = uri; g_free(uri); } } //try not to use this function for now, some info can not get correctly void FileEnumerator::setEnumerateWithInfoJob(bool query) { m_with_info_job = query; } QString FileEnumerator::getEnumerateUri() { return m_uri; } const QList> FileEnumerator::getChildren() { //m_children_uris->removeDuplicates(); //qDebug()<<"FileEnumerator::getChildren():"; QList> children; for (auto uri : *m_children_uris) { //uri reencode to prevent the existence of GBK strings if(uri.startsWith("file:///media/")){ char* fileName = g_filename_from_uri(uri.toUtf8().constData(),nullptr,nullptr); if(fileName){ char* fileUri = g_filename_to_uri(fileName,nullptr,nullptr); if(fileUri){ uri = fileUri; g_free(fileUri); } g_free(fileName); } } auto file_info = FileInfo::fromUri(uri); children << file_info; } return children; } void FileEnumerator::cancel() { g_cancellable_cancel(m_cancellable); g_object_unref(m_cancellable); m_cancellable = g_cancellable_new(); m_children_uris->clear(); Q_EMIT this->cancelled(); //Q_EMIT enumerateFinished(false); } void FileEnumerator::prepare() { g_file_enumerate_children_async(m_root_file, G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, m_cancellable, GAsyncReadyCallback(prepare_enumerate_callback), this); } GFile *FileEnumerator::enumerateTargetFile() { GFileInfo *info = g_file_query_info(m_root_file, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, G_FILE_QUERY_INFO_NONE, nullptr, nullptr); char *uri = nullptr; uri = g_file_info_get_attribute_as_string(info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); g_object_unref(info); GFile *target = nullptr; if (uri) { //qDebug()<<"enumerateTargetFile"<code == G_IO_ERROR_CANCELLED) { g_error_free(err); return nullptr; } if (err) { //do not send prepared(err) here, wait handle err finished. p_this->handleError(err); g_error_free(err); } else { //even though there is no err, we still need to wait a little while //for confirming other object will recieve this signal, you can aslo //connect prepared signal before prepared() method to confirm that. //Q_EMIT prepared(nullptr); g_object_unref(enumerator); Q_EMIT p_this->prepared(nullptr); } return nullptr; } void FileEnumerator::enumerateSync() { m_idle->start(1000); GFile *target = enumerateTargetFile(); GFileEnumerator *enumerator = g_file_enumerate_children(target, G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NONE, m_cancellable, nullptr); if (enumerator) { enumerateChildren(enumerator); g_file_enumerator_close_async(enumerator, 0, nullptr, nullptr, nullptr); g_object_unref(enumerator); } else { Q_EMIT this->enumerateFinished(false); } g_object_unref(target); } /*! * \brief FileEnumerator::handleError * \param err * \bug * Some special vfs, such as mtp, can not be handled correctly. * \todo * Handle mtp, ptp correctly. */ void FileEnumerator::handleError(GError *err) { if (!m_is_custom_error_handler_initialized) { auto vfsManager = VFSPluginManager::getInstance(); for (auto plugin : vfsManager->registeredPlugins()) { auto customErrorHandler = plugin->customErrorHandler(); if (customErrorHandler) { customErrorHandler->setParent(this); for (int supportedErrorCode : customErrorHandler->errorCodeSupportHandling()) { m_custom_error_handlers.insert(supportedErrorCode, customErrorHandler); } //FIXME: connect(customErrorHandler, &CustomErrorHandler::finished, this, [=]{ this->prepared(nullptr, this->getEnumerateUri()); }); connect(customErrorHandler, &CustomErrorHandler::cancelled, this, [=]{ cancel(); deleteLater(); }); connect(customErrorHandler, &CustomErrorHandler::failed, this, [=](const QString &message){ cancel(); deleteLater(); QMessageBox::critical(0, 0, message); }); } } m_is_custom_error_handler_initialized = true; } if (m_custom_error_handlers.contains(err->code)) { // enter custom error handler. auto customErrorHandler = m_custom_error_handlers.value(err->code); customErrorHandler->handleCustomError(getEnumerateUri(), err->code); return; } qDebug()<<"handleError"<code<message; switch (err->code) { case G_IO_ERROR_NOT_DIRECTORY: { // auto uri = g_file_get_uri(m_root_file); // //FIXME: replace BLOCKING api in ui thread. // auto targetUri = FileUtils::getTargetUri(uri); // if (uri) { // g_free(uri); // } // if (!targetUri.isEmpty()) { // prepared(nullptr, targetUri); // return; // } bool isMountable = false; //FIXME: replace BLOCKING api in ui thread. Done isMountable = FileInfo::fromGFile(m_root_file).get()->canMount(); // GFileInfo *file_mount_info = g_file_query_info(m_root_file, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT, // G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); // if (file_mount_info) { // isMountable = g_file_info_get_attribute_boolean(file_mount_info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT); // g_object_unref(file_mount_info); // } if (isMountable) { g_file_mount_mountable(m_root_file, G_MOUNT_MOUNT_NONE, nullptr, m_cancellable, GAsyncReadyCallback(mount_mountable_callback), this); } else { g_file_mount_enclosing_volume(m_root_file, G_MOUNT_MOUNT_NONE, nullptr, m_cancellable, GAsyncReadyCallback(mount_enclosing_volume_callback), this); } break; } case G_IO_ERROR_NOT_MOUNTED: //we first trying mount volume without mount operation, //because we might have saved password of target server. g_file_mount_enclosing_volume(m_root_file, G_MOUNT_MOUNT_NONE, nullptr, m_cancellable, GAsyncReadyCallback(mount_enclosing_volume_callback), this); break; case G_IO_ERROR_NOT_SUPPORTED: Peony::AudioPlayManager::getInstance()->playWarningAudio(); Q_EMIT prepared(GErrorWrapper::wrapFrom(g_error_copy(err)), nullptr, true); //QMessageBox::critical(nullptr, tr("Error"), err->message); break; case G_IO_ERROR_EXISTS: { QString str_error = QObject::tr("file not found"); Q_EMIT prepared(GErrorWrapper::wrapFrom(g_error_new(G_IO_ERROR, G_IO_ERROR_EXISTS, "%s\n", str_error.toUtf8().constData())), nullptr, true); break; } case G_IO_ERROR_PERMISSION_DENIED: { //emit error message to upper levels to process QString str_error = QObject::tr("permission denied"); Q_EMIT prepared(GErrorWrapper::wrapFrom(g_error_new(G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED, "%s\n", str_error.toUtf8().constData())), nullptr, true); break; } case G_IO_ERROR_NOT_FOUND: { QString str_error = QObject::tr("file not found"); Q_EMIT prepared(GErrorWrapper::wrapFrom(g_error_new(G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "%s\n", str_error.toUtf8().constData())), nullptr, true); //processed in file-item, comment to fix duplicated prompt //QMessageBox::critical(nullptr, tr("Error"), tr("Did not find target path, do you move or deleted it?")); break; } default: Q_EMIT prepared(GErrorWrapper::wrapFrom(g_error_copy(err)), nullptr, true); break; } } void FileEnumerator::enumerateAsync() { m_idle->start(1000); // query directory info first auto infoJob = new FileInfoJob(m_uri); infoJob->setAutoDelete(true); connect(infoJob, &FileInfoJob::queryAsyncFinished, this, [=](){ //auto uri = g_file_get_uri(m_root_file); //auto path = g_file_get_path(m_root_file); g_file_enumerate_children_async(m_root_file, m_with_info_job? "*": G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, m_cancellable, GAsyncReadyCallback(find_children_async_ready_callback), this); }); infoJob->queryAsync(); } void FileEnumerator::enumerateChildren(GFileEnumerator *enumerator) { GFileInfo *info = nullptr; GFile *child = nullptr; info = g_file_enumerator_next_file(enumerator, m_cancellable, nullptr); if (!info) { Q_EMIT enumerateFinished(false); return; } while (info) { child = g_file_enumerator_get_child(enumerator, info); char *uri = g_file_get_uri(child); char *path = g_file_get_path(child); g_object_unref(child); QUrl url = QUrl(QString(uri)); //qDebug()<code != 0) { auto message = err->message; if (err->code == G_IO_ERROR_CANCELLED) { g_error_free(err); return nullptr; } qDebug()<code<message; if (!qobject_cast(p_this)) { g_error_free(err); return nullptr; } auto err_data = GErrorWrapper::wrapFrom(err); Q_EMIT p_this->prepared(err_data); } else { Q_EMIT p_this->prepared(nullptr); if (err) { g_error_free(err); } } if (target) { g_object_unref(target); } return nullptr; } GAsyncReadyCallback FileEnumerator::mount_enclosing_volume_callback(GFile *file, GAsyncResult *res, FileEnumerator *p_this) { //qDebug()<<"mount_enclosing_volume_callback"; GError *err = nullptr; if (g_file_mount_enclosing_volume_finish (file, res, &err)) { if (err) { if (err->code == G_IO_ERROR_CANCELLED) { g_error_free (err); return nullptr; } qDebug()<<"mount successed, err:"<code<message; Q_EMIT p_this->prepared(GErrorWrapper::wrapFrom(err), nullptr, true); } else { Q_EMIT p_this->prepared(); } } else { if (err) { if (err->code == G_IO_ERROR_CANCELLED) { return nullptr; } if (err->code == G_IO_ERROR_ALREADY_MOUNTED) { Q_EMIT p_this->prepared(GErrorWrapper::wrapFrom(err)); return nullptr; } qDebug()<<"mount failed, err:"<code<message; //show the connect dialog if (!p_this->m_root_file) { //critical. return nullptr; } char *uri = g_file_get_uri(file); MountOperation *op = new MountOperation(uri); op->setAutoDelete(); g_free(uri); //op->setAutoDelete(); p_this->connect(op, &MountOperation::cancelled, p_this, [p_this]() { Q_EMIT p_this->enumerateFinished(false); }); p_this->connect(op, &MountOperation::finished, p_this, [=](const std::shared_ptr &finished_err) { if (finished_err) { qDebug()<<"finished err:"<code()<message(); if (finished_err->code() == G_IO_ERROR_PERMISSION_DENIED) { p_this->enumerateFinished(false); Peony::AudioPlayManager::getInstance()->playWarningAudio(); QMessageBox::critical(nullptr, tr("Error"), finished_err->message()); return; } Q_EMIT p_this->prepared(finished_err); } else { Q_EMIT p_this->prepared(nullptr); } }); op->start(); } } return nullptr; } GAsyncReadyCallback FileEnumerator::find_children_async_ready_callback(GFile *file, GAsyncResult *res, FileEnumerator *p_this) { GError *err = nullptr; GFileEnumerator *enumerator = g_file_enumerate_children_finish(file, res, &err); if (err) { if (err->code == G_IO_ERROR_CANCELLED) { g_error_free(err); return nullptr; } qDebug()<<"find children async err:"<code<message; //NOTE: if the enumerator file has target uri, but target uri is not mounted, //it should be handled. //This nearly won't happend in local, but in a network server it might. if (err->code == G_IO_ERROR_NOT_MOUNTED) { g_object_unref(p_this->m_root_file); p_this->m_root_file = g_file_dup(file); //p_this->prepare(); g_error_free(err); return nullptr; } g_error_free(err); } if (!enumerator) { if (qobject_cast(p_this)) Q_EMIT p_this->enumerateFinished(false); return nullptr; } // g_file_enumerator_next_files_async(enumerator, PEONY_FIND_NEXT_FILES_BATCH_SIZE, G_PRIORITY_DEFAULT, p_this->m_cancellable, GAsyncReadyCallback(enumerator_next_files_async_ready_callback), p_this); g_object_unref(enumerator); return nullptr; } GAsyncReadyCallback FileEnumerator::enumerator_next_files_async_ready_callback(GFileEnumerator *enumerator, GAsyncResult *res, FileEnumerator *p_this) { GError *err = nullptr; GList *files = g_file_enumerator_next_files_finish(enumerator, res, &err); if (err) { if (err->code == G_IO_ERROR_CANCELLED) { g_error_free(err); return nullptr; } } auto errPtr = GErrorWrapper::wrapFrom(err); if (!files && !err) { //if a directory children count is same with BATCH_SIZE, //just send finished signal. qDebug()<<"no more files"<(p_this)) Q_EMIT p_this->enumerateFinished(true); return nullptr; } if (!files && err) { //critical return nullptr; } if (err) { qDebug()<<"next_files_async:"<code<message; } GList *l = files; QStringList uriList; int files_count = 0; while (l) { GFileInfo *info = static_cast(l->data); GFile *file = g_file_enumerator_get_child(enumerator, info); char *uri = g_file_get_uri(file); char *path = g_file_get_path(file); g_object_unref(file); //qDebug()<m_cache_uris)<m_cache_uris)<m_cache_uris)<m_with_info_job) { infoJob.refreshInfoContents(info); } p_this->m_cached_infos<next; } g_list_free_full(files, g_object_unref); //Q_EMIT p_this->childrenUpdated(uriList); if (files_count == PEONY_FIND_NEXT_FILES_BATCH_SIZE) { //have next files, countinue. g_file_enumerator_next_files_async(enumerator, PEONY_FIND_NEXT_FILES_BATCH_SIZE, G_PRIORITY_DEFAULT, p_this->m_cancellable, GAsyncReadyCallback(enumerator_next_files_async_ready_callback), p_this); } else { //no next files, emit finished. //qDebug()<<"async enumerateFinished"; if (qobject_cast(p_this)) Q_EMIT p_this->enumerateFinished(true); } return nullptr; } peony/libpeony-qt/global-settings.cpp0000644000175000017500000002201314205115226016671 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "global-settings.h" #include #include #include #include #include using namespace Peony; static GlobalSettings *global_instance = nullptr; GlobalSettings *GlobalSettings::getInstance() { if (!global_instance) { global_instance = new GlobalSettings; } return global_instance; } GlobalSettings::GlobalSettings(QObject *parent) : QObject(parent) { m_settings = new QSettings("org.ukui", "peony-qt-preferences", this); //set default allow parallel if (! m_settings->allKeys().contains(ALLOW_FILE_OP_PARALLEL)) { qDebug() << "default ALLOW_FILE_OP_PARALLEL:true"; setValue(ALLOW_FILE_OP_PARALLEL, true); } //if local languege is chinese, set chinese first as deafult if (QLocale::system().name().contains("zh") && !m_settings->allKeys().contains(SORT_CHINESE_FIRST)) setValue(SORT_CHINESE_FIRST, true); for (auto key : m_settings->allKeys()) { m_cache.insert(key, m_settings->value(key)); } m_date_format = tr("yyyy/MM/dd"); m_time_format = tr("HH:mm:ss"); 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) { QString value = m_control_center_plugin->get(key).toString(); if ("hoursystem" == key) { m_cache.remove(UKUI_CONTROL_CENTER_PANEL_PLUGIN_TIME); m_cache.insert(UKUI_CONTROL_CENTER_PANEL_PLUGIN_TIME, value); Q_EMIT this->valueChanged(UKUI_CONTROL_CENTER_PANEL_PLUGIN_TIME); setTimeFormat(value); } else if (key == "date") { m_cache.remove(UKUI_CONTROL_CENTER_PANEL_PLUGIN_DATE); m_cache.insert(UKUI_CONTROL_CENTER_PANEL_PLUGIN_DATE, value); Q_EMIT this->valueChanged(UKUI_CONTROL_CENTER_PANEL_PLUGIN_DATE); setDateFormat(value); } }); QString timeValue = m_control_center_plugin->get("time").toString(); QString dateValue = m_control_center_plugin->get("date").toString(); m_cache.insert(UKUI_CONTROL_CENTER_PANEL_PLUGIN_TIME, timeValue); m_cache.insert(UKUI_CONTROL_CENTER_PANEL_PLUGIN_DATE, dateValue); setTimeFormat(timeValue); setDateFormat(dateValue); } m_cache.insert(SHOW_TRASH_DIALOG, true); m_cache.insert(SHOW_HIDDEN_PREFERENCE, false); if (QGSettings::isSchemaInstalled("org.ukui.peony.settings")) { m_peony_gsettings = new QGSettings("org.ukui.peony.settings", QByteArray(), this); connect(m_peony_gsettings, &QGSettings::changed, this, [=](const QString &key) { if (key == "showTrashDialog") { m_cache.remove(SHOW_TRASH_DIALOG); m_cache.insert(SHOW_TRASH_DIALOG, m_peony_gsettings->get(key).toBool()); } else if (SHOW_HIDDEN_PREFERENCE == key) { if (m_cache.value(key) != m_peony_gsettings->get(key).toBool()) { m_cache.remove(key); m_cache.insert(key, m_peony_gsettings->get(key).toBool()); } /* Solve the problem: When opening multiple document management, check "Show hidden files" in one document management, * but the other document management does not take effect in real time.modified by 2021/06/15 */ Q_EMIT this->valueChanged(key); } }); m_cache.remove(SHOW_TRASH_DIALOG); m_cache.insert(SHOW_TRASH_DIALOG, m_peony_gsettings->get(SHOW_TRASH_DIALOG).toBool()); m_cache.remove(SHOW_HIDDEN_PREFERENCE); m_cache.insert(SHOW_HIDDEN_PREFERENCE, m_peony_gsettings->get(SHOW_HIDDEN_PREFERENCE).toBool()); } m_cache.insert(SIDEBAR_BG_OPACITY, 50); 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 (key == "peonySideBarTransparency") { m_cache.remove(SIDEBAR_BG_OPACITY); m_cache.insert(SIDEBAR_BG_OPACITY, m_gsettings->get(key).toString()); qApp->paletteChanged(qApp->palette()); } }); m_cache.remove(SIDEBAR_BG_OPACITY); m_cache.insert(SIDEBAR_BG_OPACITY, m_gsettings->get("peonySideBarTransparency").toString()); } if (m_cache.value(DEFAULT_WINDOW_SIZE).isNull() || m_cache.value(DEFAULT_SIDEBAR_WIDTH) <= 0) { QScreen *screen=qApp->primaryScreen(); QRect geometry = screen->availableGeometry(); int default_width = geometry.width() * 2/3; int default_height = geometry.height() * 4/5; if (default_width < 850) default_width = 850; if (default_height < 850 *0.618) default_height = 850 *0.618; setValue(DEFAULT_WINDOW_SIZE, QSize(default_width, default_height)); setValue(DEFAULT_SIDEBAR_WIDTH, 210); qDebug() << "deafult set DEFAULT_SIDEBAR_WIDTH:"<<210; } if (m_cache.value(DEFAULT_VIEW_ID).isNull()) { setValue(DEFAULT_VIEW_ID, "Icon View"); } if (m_cache.value(SORT_ORDER).isNull()){ setValue(SORT_ORDER, Qt::AscendingOrder); } if (m_cache.value(SORT_COLUMN).isNull()){ setValue(SORT_COLUMN, 0); } if (m_cache.value(DEFAULT_VIEW_ZOOM_LEVEL).isNull()) { setValue(DEFAULT_VIEW_ZOOM_LEVEL, 25); } if (m_cache.value(REMOTE_SERVER_REMOTE_IP).isNull()) { setValue(REMOTE_SERVER_REMOTE_IP, QVariant(QList())); } } GlobalSettings::~GlobalSettings() { } const QVariant GlobalSettings::getValue(const QString &key) { return m_cache.value(key); } bool GlobalSettings::isExist(const QString &key) { return !m_cache.value(key).isNull(); } void GlobalSettings::reset(const QString &key) { m_cache.remove(key); QtConcurrent::run([=]() { if (m_mutex.tryLock(1000)) { m_settings->remove(key); m_settings->sync(); m_mutex.unlock(); } }); Q_EMIT this->valueChanged(key); } void GlobalSettings::resetAll() { QStringList tmp = m_cache.keys(); m_cache.clear(); for (auto key : tmp) { Q_EMIT this->valueChanged(key); } QtConcurrent::run([=]() { if (m_mutex.tryLock(1000)) { m_settings->clear(); m_settings->sync(); m_mutex.unlock(); } }); } void GlobalSettings::setValue(const QString &key, const QVariant &value) { m_cache.remove(key); m_cache.insert(key, value); QtConcurrent::run([=]() { if (m_mutex.tryLock(1000)) { m_settings->setValue(key, value); m_settings->sync(); m_mutex.unlock(); } }); } void GlobalSettings::forceSync(const QString &key) { m_settings->sync(); if (key.isNull()) { m_cache.clear(); for (auto key : m_settings->allKeys()) { m_cache.insert(key, m_settings->value(key)); } } else { m_cache.remove(key); m_cache.insert(key, m_settings->value(key)); } } void GlobalSettings::slot_updateRemoteServer(const QString& server, bool add) { Q_EMIT signal_updateRemoteServer(server, add); } void GlobalSettings::setTimeFormat(const QString &value) { if (value == "12"){ m_time_format = tr("hh:mm:ss AP"); } else{ m_time_format = tr("HH:mm:ss"); } } void GlobalSettings::setDateFormat(const QString &value) { if (value == "cn"){ m_date_format = tr("yyyy/MM/dd"); } else{ m_date_format = tr("yyyy-MM-dd"); } } QString GlobalSettings::getSystemTimeFormat() { m_system_time_format = m_date_format + " " + m_time_format; return m_system_time_format; } void GlobalSettings::setGSettingValue(const QString &key, const QVariant &value) { if (!m_peony_gsettings) return; const QStringList list = m_peony_gsettings->keys(); if (!list.contains(key)) return; m_peony_gsettings->set(key, value); m_cache.remove(key); m_cache.insert(key, m_peony_gsettings->get(key)); } peony/libpeony-qt/plugin-manager.cpp0000644000175000017500000001237414205115226016512 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "plugin-manager.h" #include "menu-plugin-manager.h" #include "directory-view-factory-manager.h" #include "preview-page-factory-manager.h" #include "directory-view-plugin-iface.h" #include "directory-view-plugin-iface2.h" #include "preview-page-plugin-iface.h" #include "style-plugin-iface.h" #include "vfs-plugin-manager.h" #include "properties-window.h" //properties factory manager define is in this header #include "properties-window-tab-page-plugin-iface.h" #include "directory-view-widget.h" #include "global-settings.h" #include #include #include #include #include using namespace Peony; static PluginManager *global_instance = nullptr; PluginManager::PluginManager(QObject *parent) : QObject(parent) { //FIXME: we have to ensure that internal factory being registered successfully. PropertiesWindowPluginManager::getInstance(); MenuPluginManager::getInstance(); DirectoryViewFactoryManager2::getInstance(); PreviewPageFactoryManager::getInstance(); VFSPluginManager::getInstance(); QDir pluginsDir(PLUGIN_INSTALL_DIRS); // if (COMMERCIAL_VERSION) // pluginsDir = QDir("/usr/lib/peony-qt-extensions"); pluginsDir.setFilter(QDir::Files); qDebug()<(plugin); if (!piface) continue; m_hash.insert(piface->name(), piface); switch (piface->pluginType()) { case PluginInterface::MenuPlugin: { MenuPluginInterface *menuPlugin = dynamic_cast(piface); MenuPluginManager::getInstance()->registerPlugin(menuPlugin); break; } case PluginInterface::PreviewPagePlugin: { PreviewPagePluginIface *previewPageFactory = dynamic_cast(plugin); PreviewPageFactoryManager::getInstance()->registerFactory(previewPageFactory->name(), previewPageFactory); break; } case PluginInterface::PropertiesWindowPlugin: { PropertiesWindowTabPagePluginIface *propertiesWindowTabPageFactory = dynamic_cast(plugin); PropertiesWindowPluginManager::getInstance()->registerFactory(propertiesWindowTabPageFactory); break; } case PluginInterface::ColumnProviderPlugin: { //FIXME: break; } case PluginInterface::StylePlugin: { /*! \todo manage the style plugin */ auto styleProvider = dynamic_cast(plugin); QApplication::setStyle(styleProvider->getStyle()); break; } case PluginInterface::DirectoryViewPlugin2: { auto p = dynamic_cast(plugin); DirectoryViewFactoryManager2::getInstance()->registerFactory(p->viewIdentity(), p); break; } case PluginInterface::VFSPlugin: { auto p = dynamic_cast(plugin); VFSPluginManager::getInstance()->registerPlugin(p); break; } default: break; } } } PluginManager::~PluginManager() { m_hash.clear(); MenuPluginManager::getInstance()->close(); //FIXME: use private deconstructor. DirectoryViewFactoryManager2::getInstance()->deleteLater(); PreviewPageFactoryManager::getInstance()->deleteLater(); } PluginManager *PluginManager::getInstance() { if (!global_instance) { global_instance = new PluginManager; } return global_instance; } void PluginManager::setPluginEnableByName(const QString &name, bool enable) { m_hash.value(name)->setEnable(enable); } void PluginManager::close() { if (global_instance) global_instance->deleteLater(); } void PluginManager::init() { PluginManager::getInstance(); } peony/libpeony-qt/file-launcher/0000755000175000017500000000000014205115226015607 5ustar fengfengpeony/libpeony-qt/file-launcher/file-launch-manager.cpp0000644000175000017500000001730414205101223022107 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-launch-manager.h" #include "file-utils.h" #include "file-info.h" #include "file-info-job.h" #include #include "file-launch-action.h" #include #include #include using namespace Peony; FileLaunchManager::FileLaunchManager(QObject *parent) : QObject(parent) { } FileLaunchAction *FileLaunchManager::getDefaultAction(const QString &uri) { //FIXME: replace BLOCKING api in ui thread. auto info = FileInfo::fromUri(uri); QString mimeType = info->mimeType(); if (mimeType.isEmpty()) { FileInfoJob job(info); job.querySync(); mimeType = info->mimeType(); } if (info->canExecute() && info->uri().endsWith(".desktop")) { QUrl url = uri; auto path = url.path(); GDesktopAppInfo *info = g_desktop_app_info_new_from_filename(path.toUtf8().constData()); FileLaunchAction *action = new FileLaunchAction(uri, G_APP_INFO(info)); g_object_unref(info); return action; } else { GError *error = NULL; GAppInfo *info = NULL; /* * g_app_info_get_default_for_type function get wrong default app, so we get the * default app info from mimeapps.list, and chose the right default app for mimeType file */ QString mimeAppsListPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/.config/mimeapps.list"; GKeyFile *keyfile = g_key_file_new (); gboolean ret = g_key_file_load_from_file (keyfile, mimeAppsListPath.toUtf8(), G_KEY_FILE_NONE, &error); if (false == ret) { qWarning()<<"load mimeapps list error msg"<message; info = g_app_info_get_default_for_type(mimeType.toUtf8().constData(), false); g_error_free(error); } else { gchar *desktopApp = g_key_file_get_string (keyfile, "Default Applications", mimeType.toUtf8(), &error); if (NULL != desktopApp) { info = (GAppInfo*)g_desktop_app_info_new(desktopApp); g_free (desktopApp); } else { info = g_app_info_get_default_for_type(mimeType.toUtf8().constData(), false); } } g_key_file_free (keyfile); FileLaunchAction *action = new FileLaunchAction(uri, info); g_object_unref(info); return action; } } const QList FileLaunchManager::getRecommendActions(const QString &uri) { //FIXME: replace BLOCKING api in ui thread. auto info = FileInfo::fromUri(uri); QString mimeType = info->mimeType(); if (mimeType.isEmpty()) { FileInfoJob job(info); job.querySync(); mimeType = info->mimeType(); } GList *app_infos = g_app_info_get_recommended_for_type(mimeType.toUtf8().constData()); GList *l = app_infos; QList actions; while (l) { auto app_info = static_cast(l->data); actions<next; } return actions; } const QList FileLaunchManager::getFallbackActions(const QString &uri) { //FIXME: replace BLOCKING api in ui thread. auto info = FileInfo::fromUri(uri); QString mimeType = info->mimeType(); if (mimeType.isEmpty()) { FileInfoJob job(info); job.querySync(); mimeType = info->mimeType(); } GList *app_infos = g_app_info_get_fallback_for_type(mimeType.toUtf8().constData()); GList *l = app_infos; QList actions; while (l) { auto app_info = static_cast(l->data); actions<next; } return actions; } const QList FileLaunchManager::getAllActionsForType(const QString &uri) { //FIXME: replace BLOCKING api in ui thread. auto info = FileInfo::fromUri(uri); QString mimeType = info->mimeType(); if (mimeType.isEmpty()) { FileInfoJob job(info); job.querySync(); mimeType = info->mimeType(); } GList *app_infos = g_app_info_get_all_for_type(mimeType.toUtf8().constData()); GList *l = app_infos; QList actions; while (l) { auto app_info = static_cast(l->data); //only show available applications if (g_app_info_should_show(app_info)) actions<next; } return actions; } const QList FileLaunchManager::getAllActions(const QString &uri) { GList *app_infos = g_app_info_get_all(); GList *l = app_infos; QList actions; while (l) { auto app_info = static_cast(l->data); //only show available applications if (g_app_info_should_show(app_info)) actions<next; } return actions; } void FileLaunchManager::openSync(const QString &uri, bool forceWithArg, bool skipDialog) { QString tmp = uri; auto targetUri = FileUtils::getTargetUri(uri); if (targetUri.isNull()) { tmp = targetUri; } auto action = getDefaultAction(tmp); action->lauchFileSync(forceWithArg, skipDialog); action->deleteLater(); } void FileLaunchManager::openAsync(const QString &uri, bool forceWithArg, bool skipDialog) { QString tmp = uri; //FIXME: replace BLOCKING api in ui thread. auto targetUri = FileUtils::getTargetUri(uri); if (!targetUri.isNull()) { tmp = targetUri; qDebug()<<"open async"<lauchFileAsync(forceWithArg, skipDialog); action->deleteLater(); } void FileLaunchManager::openAsync(const QStringList &files, bool forceWithArg, bool skipDialog) { QStringList targets; for (auto uri : files) { auto target = FileUtils::getTargetUri(uri); if (!target.isEmpty()) { targets.append(target); } else { targets.append(uri); } } auto action = getDefaultAction(targets.first()); action->lauchFilesAsync(targets, forceWithArg, skipDialog); action->deleteLater(); } void FileLaunchManager::setDefaultLauchAction(const QString &uri, FileLaunchAction *action) { //FIXME: replace BLOCKING api in ui thread. auto info = FileInfo::fromUri(uri); if (info->mimeType().isEmpty()) { FileInfoJob job(info); job.querySync(); } GError *err = nullptr; bool ret = g_app_info_set_as_default_for_type(action->gAppInfo(), info->mimeType().toUtf8(), &err); if (false == ret) { qDebug()<<"set default app failed, err code" <code <<"err msg"<< err->message; g_error_free(err); } } peony/libpeony-qt/file-launcher/file-lauch-dialog.h0000644000175000017500000000315514205115226021232 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILELAUCHDIALOG_H #define FILELAUCHDIALOG_H #include #include "peony-core_global.h" #include class QVBoxLayout; class QListWidget; class QCheckBox; class QDialogButtonBox; class QListWidgetItem; namespace Peony { class FileLaunchAction; /*! * \brief The FileLauchDialog class * provides the dialog for choosing which application to open a file. */ class PEONYCORESHARED_EXPORT FileLauchDialog : public QDialog { Q_OBJECT public: explicit FileLauchDialog(const QString &uri, QWidget *parent = nullptr); QSize sizeHint() const override { return QSize(400, 600); } private: QVBoxLayout *m_layout; QListWidget *m_view; QCheckBox *m_check_box; QDialogButtonBox *m_button_box; QString m_uri; QHash m_hash; }; } #endif // FILELAUCHDIALOG_H peony/libpeony-qt/file-launcher/file-launch-action.h0000644000175000017500000000513614205101223021417 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILELAUNCHACTION_H #define FILELAUNCHACTION_H #include #include #include "peony-core_global.h" namespace Peony { /*! * \brief The FileLaunchAction class * \details * This class is used to launch a file with a specific application info. * \todo * add error reprot GUI. */ class PEONYCORESHARED_EXPORT FileLaunchAction : public QAction { Q_OBJECT public: explicit FileLaunchAction(const QString &uri, GAppInfo *app_info, bool forceWithArg = false, QObject *parent = nullptr); ~FileLaunchAction() override; const QString getUri(); bool isDesktopFileAction(); const QString getAppInfoName(); const QString getAppInfoDisplayName(); GAppInfo *gAppInfo() { return m_app_info; } protected: bool isValid(); void execFile(); void execFileInterm(); public Q_SLOTS: void lauchFileSync(bool forceWithArg = false, bool skipDialog = true); void lauchFileAsync(bool forceWithArg = false, bool skipDialog = true); void lauchFilesAsync(const QStringList files, bool forceWithArg = false, bool skipDialog = true); bool isExcuteableFile(QString fileType); private: QString m_uri; bool m_is_desktop_file; GAppInfo *m_app_info; QIcon m_icon; QString m_info_name; QString m_info_display_name; QStringList m_executable_type = {"application/x-shellscript", "application/x-executable", "application/x-perl", "application/x-sharedlib", "text/x-python3", "application/javascript", "application/x-ruby"}; /*! * \brief m_force_with_arg * \value true for forcing to execute action asynchonously. */ bool m_force_with_arg = false; }; } #endif // FILELAUNCHACTION_H peony/libpeony-qt/file-launcher/file-lauch-dialog.cpp0000644000175000017500000000712414205115226021565 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-lauch-dialog.h" #include "file-launch-action.h" #include "file-launch-manager.h" #include "file-info.h" #include #include #include #include #include #include #include using namespace Peony; FileLauchDialog::FileLauchDialog(const QString &uri, QWidget *parent) : QDialog(parent) { m_layout = new QVBoxLayout(this); setLayout(m_layout); setWindowTitle(tr("Applications")); m_layout->addWidget(new QLabel(tr("Choose an Application to open this file"), this)); m_view = new QListWidget(this); m_view->setIconSize(QSize(48, 48)); m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_layout->addWidget(m_view, 1); m_check_box = new QCheckBox(tr("Set as Default"), this); m_layout->addWidget(m_check_box); m_button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); 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")); m_uri = uri; auto actions = FileLaunchManager::getAllActions(uri); for (auto action : actions) { //fix show no icon app in list issue, bug#18171 if (action->icon().isNull()) continue; //FIXME should have a spcific rule to decide which kind of app can show //fix show uninstall app in list issue, link to bug#80233 if (action->icon().name().contains("uninstall")) continue; action->setParent(this); //qDebug() << "lauch actions:" <icon() <iconText() <text(); auto item = new QListWidgetItem(!action->icon().isNull()? action->icon(): QIcon::fromTheme("application-x-desktop"), action->text(), m_view); m_view->addItem(item); m_hash.insert(item, action); } connect(this, &QDialog::accepted, [=]() { if (m_view->currentItem()) { auto action = m_hash.value(m_view->currentItem()); if (m_check_box->isChecked()) { FileLaunchManager::setDefaultLauchAction(m_uri, action); } action->lauchFileAsync(true); } else { FileLaunchManager::openAsync(m_uri); } }); connect(m_button_box, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(m_button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); //FIXME: replace BLOCKING api in ui thread. auto info = FileInfo::fromUri(uri); if (info->isDir() || info->isDesktopFile()) { m_check_box->setEnabled(false); } } peony/libpeony-qt/file-launcher/file-launch-manager.h0000644000175000017500000000440614205101223021553 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILELAUNCHMANAGER_H #define FILELAUNCHMANAGER_H #include #include "peony-core_global.h" namespace Peony { class FileLaunchAction; /*! * \brief The FileLaunchManager class * \details * This class provides the convenient way for file's uri and lauch action. * \todo * support open multi-files. */ class PEONYCORESHARED_EXPORT FileLaunchManager : public QObject { Q_OBJECT public: static FileLaunchAction *getDefaultAction(const QString &uri); static const QList getRecommendActions(const QString &uri); static const QList getFallbackActions(const QString &uri); static const QList getAllActionsForType(const QString &uri); static const QList getAllActions(const QString &uri); /*! * \brief setDefaultLauchAction * \param uri * \param action * \note * set the files default lauch action which * have same mime type with the file's * type passing uri represent. */ static void setDefaultLauchAction(const QString &uri, FileLaunchAction *action); static void openSync(const QString &uri, bool forceWithArg = false, bool skipDialog = true); static void openAsync(const QString &uri, bool forceWithArg = false, bool skipDialog = true); static void openAsync(const QStringList &files, bool forceWithArg = false, bool skipDialog = true); private: explicit FileLaunchManager(QObject *parent = nullptr); }; } #endif // FILELAUNCHMANAGER_H peony/libpeony-qt/file-launcher/file-launch-action.cpp0000644000175000017500000004507214205115226021765 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #include "file-launch-action.h" #include #include "file-info.h" #include "file-info-job.h" #include "file-operation-utils.h" #include "audio-play-manager.h" #include #include #include #include #include #include #include using namespace Peony; FileLaunchAction::FileLaunchAction(const QString &uri, GAppInfo *app_info, bool forceWithArg, QObject *parent) : QAction(parent) { m_uri = uri; m_app_info = static_cast(g_object_ref(app_info)); m_force_with_arg = forceWithArg; if (!isValid()) return; GThemedIcon *icon = G_THEMED_ICON(g_app_info_get_icon(m_app_info)); const char * const * icon_names = g_themed_icon_get_names(icon); if (icon_names) m_icon = QIcon::fromTheme(*icon_names); setIcon(m_icon); m_info_name = g_app_info_get_name(m_app_info); setText(m_info_name); m_info_display_name = g_app_info_get_display_name(m_app_info); connect(this, &QAction::triggered, [=]() { this->lauchFileAsync(m_force_with_arg, true); }); } FileLaunchAction::~FileLaunchAction() { if (m_app_info) g_object_unref(m_app_info); } const QString FileLaunchAction::getUri() { return m_uri; } bool FileLaunchAction::isDesktopFileAction() { //FIXME: replace BLOCKING api in ui thread. auto info = FileInfo::fromUri(m_uri); if (info->isEmptyInfo()) { FileInfoJob j(info); j.querySync(); } return info->isDesktopFile(); } const QString FileLaunchAction::getAppInfoName() { return m_info_name; } const QString FileLaunchAction::getAppInfoDisplayName() { return m_info_display_name; } bool FileLaunchAction::isExcuteableFile(QString fileType) { if (m_executable_type.contains(fileType)) return true; return false; } void FileLaunchAction::lauchFileSync(bool forceWithArg, bool skipDialog) { //FIXME: replace BLOCKING api in ui thread. auto fileInfo = FileInfo::fromUri(m_uri); if (fileInfo->isEmptyInfo()) { FileInfoJob j(fileInfo); j.querySync(); } bool executable = fileInfo->canExecute(); bool isAppImage = fileInfo->type() == "application/vnd.appimage"; bool isExecutable = isExcuteableFile(fileInfo->type()); if (isAppImage) { if (executable) { QUrl url = m_uri; auto path = url.path(); QProcess p; p.setProgram(path); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) p.startDetached(); #else p.startDetached(path); #endif return; } } if (executable && !isDesktopFileAction() && !skipDialog && isExecutable) { QMessageBox msg; auto defaultAction = msg.addButton("By Default App", QMessageBox::ButtonRole::ActionRole); auto exec = msg.addButton(tr("Execute Directly"), QMessageBox::ButtonRole::ActionRole); auto execTerm = msg.addButton(tr("Execute in Terminal"), QMessageBox::ButtonRole::ActionRole); msg.addButton(QMessageBox::Cancel); msg.setText(tr("Detected launching an executable file %1, you want?").arg(fileInfo->displayName())); msg.exec(); auto button = msg.clickedButton(); if (button == exec) { execFile(); return; } else if (button == execTerm) { execFileInterm(); return; } else if (button == defaultAction) { //skip } else { return; } } if (!isValid()) { Peony::AudioPlayManager::getInstance()->playWarningAudio(); QMessageBox::critical(nullptr, tr("Open Failed"), tr("Can not open %1, file not exist, is it deleted?").arg(m_uri)); return; } if (isDesktopFileAction() && !forceWithArg) { g_app_info_launch(m_app_info, nullptr, nullptr, nullptr); } else { GList *l = nullptr; char *uri = g_strdup(m_uri.toUtf8().constData()); l = g_list_prepend(l, uri); g_app_info_launch_uris(m_app_info, l, nullptr, nullptr); g_list_free_full(l, g_free); } return; if (isDesktopFileAction() && !forceWithArg) { auto desktop_info = G_DESKTOP_APP_INFO(m_app_info); g_desktop_app_info_launch_uris_as_manager (desktop_info, nullptr, nullptr, G_SPAWN_DEFAULT, nullptr, nullptr, nullptr, nullptr, nullptr); } else { g_app_info_launch_default_for_uri(m_uri.toUtf8().constData(), nullptr, nullptr); } } void FileLaunchAction::lauchFileAsync(bool forceWithArg, bool skipDialog) { //FIXME: replace BLOCKING api in ui thread. auto fileInfo = FileInfo::fromUri(m_uri); if (fileInfo->isEmptyInfo()) { FileInfoJob j(fileInfo); j.querySync(); } bool executable = fileInfo->canExecute(); bool isAppImage = fileInfo->type() == "application/vnd.appimage"; bool isExecutable = isExcuteableFile(fileInfo->type()); qDebug() <<"executable:" <type(); QUrl url = m_uri; if (isAppImage) { if (executable) { auto path = url.path(); QProcess p; p.setProgram(path); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) p.startDetached(); #else p.startDetached(path); #endif return; } } if (executable && !isDesktopFileAction() && !skipDialog && isExecutable) { QMessageBox msg; auto defaultAction = msg.addButton(tr("By Default App"), QMessageBox::ButtonRole::ActionRole); auto exec = msg.addButton(tr("Execute Directly"), QMessageBox::ButtonRole::ActionRole); auto execTerm = msg.addButton(tr("Execute in Terminal"), QMessageBox::ButtonRole::ActionRole); msg.addButton(QMessageBox::Cancel); msg.setWindowTitle(tr("Launch Options")); msg.setText(tr("Detected launching an executable file %1, you want?").arg(fileInfo->displayName())); msg.exec(); auto button = msg.clickedButton(); if (button == exec) { execFile(); return; } else if (button == execTerm) { execFileInterm(); return; } else if (button == defaultAction) { //skip } else { return; } } if (!isValid()) { Peony::AudioPlayManager::getInstance()->playWarningAudio(); bool isReadable = fileInfo->canRead(); if (!isReadable) { if (fileInfo->isSymbolLink()) { auto result = QMessageBox::question(nullptr, tr("Open Link failed"), tr("File not exist, do you want to delete the link file?")); if (result == QMessageBox::Yes) { qDebug() << "Delete unused symbollink."; QStringList selections; selections.push_back(m_uri); FileOperationUtils::trash(selections, true); } } else QMessageBox::critical(nullptr, tr("Open Failed"), tr("Can not open %1, Please confirm you have the right authority.").arg(url.toDisplayString())); } else if (fileInfo->isDesktopFile()) { auto result = QMessageBox::question(nullptr, tr("Open App failed"), tr("The linked app is changed or uninstalled, so it can not work correctly. \n" "Do you want to delete the link file?")); if (result == QMessageBox::Yes) { qDebug() << "Delete unused desktop file"; QStringList selections; selections.push_back(m_uri); FileOperationUtils::trash(selections, true); } } else { auto result = QMessageBox::question(nullptr, tr("Error"), tr("Can not get a default application for opening %1, do you want open it with text format?").arg(url.toDisplayString())); if (result == QMessageBox::Yes) { GAppInfo *text_info = g_app_info_get_default_for_type("text/plain", false); GList *l = nullptr; char *uri = g_strdup(m_uri.toUtf8().constData()); l = g_list_prepend(l, uri); #if GLIB_CHECK_VERSION(2, 60, 0) g_app_info_launch_uris_async(text_info, l, nullptr, nullptr, nullptr, nullptr); #else g_app_info_launch_uris(text_info, l, nullptr, nullptr); #endif g_list_free_full(l, g_free); g_object_unref(text_info); } } return; } if (isDesktopFileAction() && !forceWithArg) { #if GLIB_CHECK_VERSION(2, 60, 0) g_app_info_launch_uris_async(m_app_info, nullptr, nullptr, nullptr, nullptr, nullptr); #else g_app_info_launch_uris(m_app_info, nullptr, nullptr, nullptr); #endif } else { GList *l = nullptr; char *uri = g_strdup(m_uri.toUtf8().constData()); l = g_list_prepend(l, uri); #if GLIB_CHECK_VERSION(2, 60, 0) g_app_info_launch_uris_async(m_app_info, l, nullptr, nullptr, nullptr, nullptr); RecentVFSManager::getInstance()->insert(fileInfo.get()->uri(), fileInfo.get()->mimeType(), fileInfo.get()->displayName(), g_app_info_get_name(m_app_info)); #else g_app_info_launch_uris(m_app_info, l, nullptr, nullptr); #endif g_list_free_full(l, g_free); } return; if (isDesktopFileAction() && !forceWithArg) { auto desktop_info = G_DESKTOP_APP_INFO(m_app_info); g_desktop_app_info_launch_uris_as_manager (desktop_info, nullptr, nullptr, G_SPAWN_DEFAULT, nullptr, nullptr, nullptr, nullptr, nullptr); } else { #if GLIB_CHECK_VERSION(2, 50, 0) g_app_info_launch_default_for_uri_async(m_uri.toUtf8().constData(), nullptr, nullptr, nullptr, nullptr); #else g_app_info_launch_default_for_uri(m_uri.toUtf8().constData(), nullptr, nullptr); #endif } } void FileLaunchAction::lauchFilesAsync(const QStringList files, bool forceWithArg, bool skipDialog) { if(files.isEmpty()) return; //FIXME: replace BLOCKING api in ui thread. auto fileInfo = FileInfo::fromUri(m_uri); if (fileInfo->isEmptyInfo()) { FileInfoJob j(fileInfo); j.querySync(); } bool executable = fileInfo->canExecute(); bool isAppImage = fileInfo->type() == "application/vnd.appimage"; bool isExecutable = isExcuteableFile(fileInfo->type()); if (isAppImage) { if (executable) { QProcess p; for (auto uri:files) { auto path = ((QUrl) uri).path(); QProcess p; p.setProgram(path); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) p.startDetached(); #else p.startDetached(path); #endif } return; } } if (executable && !isDesktopFileAction() && !skipDialog && isExecutable) { QMessageBox msg; auto defaultAction = msg.addButton(tr("By Default App"), QMessageBox::ButtonRole::ActionRole); auto exec = msg.addButton(tr("Execute Directly"), QMessageBox::ButtonRole::ActionRole); auto execTerm = msg.addButton(tr("Execute in Terminal"), QMessageBox::ButtonRole::ActionRole); msg.addButton(QMessageBox::Cancel); msg.setWindowTitle(tr("Launch Options")); msg.setText(tr("Detected launching an executable file %1, you want?").arg(fileInfo->displayName())); msg.exec(); auto button = msg.clickedButton(); if (button == exec) { execFile(); return; } else if (button == execTerm) { execFileInterm(); return; } else if (button == defaultAction) { //skip } else { return; } } if (!isValid()) { Peony::AudioPlayManager::getInstance()->playWarningAudio(); bool isReadable = fileInfo->canRead(); if (!isReadable) { if (fileInfo->isSymbolLink()) { auto result = QMessageBox::question(nullptr, tr("Open Link failed"), tr("File not exist, do you want to delete the link file?")); if (result == QMessageBox::Yes) { qDebug() << "Delete unused symbollink."; QStringList selections; selections.push_back(m_uri); FileOperationUtils::trash(selections, true); } } else QMessageBox::critical(nullptr, tr("Open Failed"), tr("Can not open %1, Please confirm you have the right authority.").arg(m_uri)); } else if (fileInfo->isDesktopFile()) { auto result = QMessageBox::question(nullptr, tr("Open App failed"), tr("The linked app is changed or uninstalled, so it can not work correctly. \n" "Do you want to delete the link file?")); if (result == QMessageBox::Yes) { qDebug() << "Delete unused desktop file"; QStringList selections; selections.push_back(m_uri); FileOperationUtils::trash(selections, true); } } else { auto result = QMessageBox::question(nullptr, tr("Error"), tr("Can not get a default application for opening %1, do you want open it with text format?").arg(m_uri)); if (result == QMessageBox::Yes) { GAppInfo *text_info = g_app_info_get_default_for_type("text/plain", false); GList *l = nullptr; for (auto uri : files) { l = g_list_prepend(l, g_strdup(uri.toUtf8().constData())); } #if GLIB_CHECK_VERSION(2, 60, 0) g_app_info_launch_uris_async(text_info, l, nullptr, nullptr, nullptr, nullptr); #else g_app_info_launch_uris(text_info, l, nullptr, nullptr); #endif g_list_free_full(l, g_free); g_object_unref(text_info); } } return; } if (isDesktopFileAction() && !forceWithArg) { #if GLIB_CHECK_VERSION(2, 60, 0) g_app_info_launch_uris_async(m_app_info, nullptr, nullptr, nullptr, nullptr, nullptr); #else g_app_info_launch_uris(m_app_info, nullptr, nullptr, nullptr); #endif } else { GList *l = nullptr; for (auto uri : files) { l = g_list_prepend(l, g_strdup(uri.toUtf8().constData())); } #if GLIB_CHECK_VERSION(2, 60, 0) g_app_info_launch_uris_async(m_app_info, l, nullptr, nullptr, nullptr, nullptr); #else g_app_info_launch_uris(m_app_info, l, nullptr, nullptr); #endif g_list_free_full(l, g_free); } return; if (isDesktopFileAction() && !forceWithArg) { auto desktop_info = G_DESKTOP_APP_INFO(m_app_info); g_desktop_app_info_launch_uris_as_manager (desktop_info, nullptr, nullptr, G_SPAWN_DEFAULT, nullptr, nullptr, nullptr, nullptr, nullptr); } else { #if GLIB_CHECK_VERSION(2, 50, 0) g_app_info_launch_default_for_uri_async(m_uri.toUtf8().constData(), nullptr, nullptr, nullptr, nullptr); #else g_app_info_launch_default_for_uri(m_uri.toUtf8().constData(), nullptr, nullptr); #endif } } bool FileLaunchAction::isValid() { return G_IS_APP_INFO(m_app_info); } void FileLaunchAction::execFile() { QUrl url = m_uri; char *quote = g_shell_quote(url.path().toUtf8()); QString newDir = m_uri.section('/',0,m_uri.count('/')-1); GAppInfo *app_info = g_app_info_create_from_commandline(quote, nullptr, G_APP_INFO_CREATE_NONE, nullptr); QDir::setCurrent(QUrl(newDir).path()); g_app_info_launch(app_info, nullptr, nullptr, nullptr); QDir::setCurrent(QDir::homePath()); g_object_unref(app_info); g_free(quote); } void FileLaunchAction::execFileInterm() { QUrl url = m_uri; char *quote = g_shell_quote(url.path().toUtf8()); QString newDir = m_uri.section('/',0,m_uri.count('/')-1); GAppInfo *app_info = g_app_info_create_from_commandline(quote, nullptr, G_APP_INFO_CREATE_NEEDS_TERMINAL, nullptr); QDir::setCurrent(QUrl(newDir).path()); g_app_info_launch(app_info, nullptr, nullptr, nullptr); QDir::setCurrent(QDir::homePath()); g_object_unref(app_info); g_free(quote); } peony/libpeony-qt/file-launcher/file-launcher.pri0000644000175000017500000000037114205101223021032 0ustar fengfengINCLUDEPATH += $$PWD HEADERS += \ $$PWD/file-launch-action.h \ $$PWD/file-launch-manager.h \ $$PWD/file-lauch-dialog.h SOURCES += \ $$PWD/file-launch-action.cpp \ $$PWD/file-launch-manager.cpp \ $$PWD/file-lauch-dialog.cpp peony/libpeony-qt/model/0000755000175000017500000000000014205115226014171 5ustar fengfengpeony/libpeony-qt/model/model-test/0000755000175000017500000000000014205101223016236 5ustar fengfengpeony/libpeony-qt/model/model-test/mainwindow.h0000644000175000017500000000200114205101223020554 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); ~MainWindow(); }; #endif // MAINWINDOW_H peony/libpeony-qt/model/model-test/mainwindow.cpp0000644000175000017500000001615114205101223021122 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #include "mainwindow.h" #include #include #include #include "file-info.h" #include "file-item.h" #include "file-item-model.h" #include "file-item-proxy-filter-sort-model.h" #include "file-enumerator.h" #include "file-info-job.h" #include "gerror-wrapper.h" #include #include #include #include #include #include #include #include "file-info-manager.h" #include "file-operation-manager.h" #define TEST MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { #ifndef TEST #define TEST QLineEdit *line = new QLineEdit("file:///home/lanyue", this); QToolBar *toolbar = new QToolBar(this); toolbar->addWidget(line); addToolBar(Qt::TopToolBarArea, toolbar); //job /* connect(line, &QLineEdit::returnPressed, [=](){ auto info = Peony::FileInfo::fromUri(line->text()); Peony::FileInfoJob *job = new Peony::FileInfoJob(info); job->setAutoDelete(); job->connect(job, &Peony::FileInfoJob::queryAsyncFinished, [=](bool successed){ if (successed) { qDebug()<displayName(); } }); job->queryAsync(); }); */ //enumerator /* connect(line, &QLineEdit::returnPressed, [=](){ Peony::FileEnumerator *enumerator = new Peony::FileEnumerator; enumerator->setEnumerateDirectory(line->text()); connect(enumerator, &Peony::FileEnumerator::prepared, [=](std::shared_ptr err){ if (err) { qDebug()<message(); return ; } connect(enumerator, &Peony::FileEnumerator::enumerateFinished, [=](bool successed){ if (successed) { auto files = enumerator->getChildren(); for (auto file : files) { qDebug()<uri(); } delete enumerator; } }); enumerator->enumerateAsync(); }); enumerator->prepare(); }); */ //model/view connect(line, &QLineEdit::returnPressed, [=]() { Peony::FileItemModel *model = new Peony::FileItemModel(this); Peony::FileItem *item = new Peony::FileItem(Peony::FileInfo::fromUri(line->text().toUtf8().constData()), nullptr, model, this); model->setRootItem(item); Peony::FileItemProxyFilterSortModel *pm = new Peony::FileItemProxyFilterSortModel; pm->setSourceModel(model); //icon view, dnd. QListView *lv = new QListView; lv->setModel(pm); lv->setViewMode(QListView::IconMode); lv->setDragDropMode(QListView::DragDrop); lv->setDragEnabled(true); lv->setDefaultDropAction(Qt::MoveAction); lv->setAcceptDrops(true); lv->setSelectionMode(QAbstractItemView::ExtendedSelection); //layout lv->setGridSize(QSize(96, 96)); lv->setIconSize(QSize(48, 48)); lv->setResizeMode(QListView::Adjust); //lv->setUniformItemSizes(true); lv->setWordWrap(true); //undo redo action QAction *undoAction = new QAction(QIcon::fromTheme("undo"), tr("undo"), lv); undoAction->setShortcut(QKeySequence::Undo); QAction *redoAction = new QAction(QIcon::fromTheme("redo"), tr("redo"), lv); redoAction->setShortcut(QKeySequence::Redo); connect(undoAction, &QAction::triggered, [=]() { auto fileOpMgr = Peony::FileOperationManager::getInstance(); fileOpMgr->undo(); }); connect(redoAction, &QAction::triggered, [=]() { auto fileOpMgr = Peony::FileOperationManager::getInstance(); fileOpMgr->redo(); }); lv->addAction(undoAction); lv->addAction(redoAction); connect(lv, &QListView::doubleClicked, [=](const QModelIndex &index) { lv->setWindowTitle(index.data().toString()); model->setRootIndex(index); //pm->update(); }); connect(model, &Peony::FileItemModel::findChildrenFinished, [=]() { pm->sort(0); }); lv->show(); //tree view, expandable /* QTreeView *v = new QTreeView(); v->setAttribute(Qt::WA_DeleteOnClose); v->setModel(model); model->setParent(v); v->show(); connect(v, &QTreeView::expanded, [=](const QModelIndex &index){ auto item = model->itemFromIndex(index); item->findChildrenAsync(); }); connect(v, &QTreeView::collapsed, [=](const QModelIndex &index){ auto item = model->itemFromIndex(index); item->clearChildren(); }); QTreeView *pv = new QTreeView; Peony::FileItemProxyFilterSortModel *proxy_model = new Peony::FileItemProxyFilterSortModel(pv); proxy_model->setSourceModel(model); pv->setAttribute(Qt::WA_DeleteOnClose); pv->setSortingEnabled(true); pv->setModel(proxy_model); connect(pv, &QTreeView::expanded, [=](const QModelIndex &proxyIndex){ auto item = proxy_model->itemFromIndex(proxyIndex); item->findChildrenAsync(); }); connect(pv, &QTreeView::collapsed, [=](const QModelIndex &proxyIndex){ auto index = proxy_model->getSourceIndex(proxyIndex); auto item = model->itemFromIndex(index); item->clearChildren(); }); pv->show(); pv->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); connect(model, &Peony::FileItemModel::findChildrenStarted, [pv](){ QCursor c; c.setShape(Qt::WaitCursor); pv->setCursor(c); }); connect(model, &Peony::FileItemModel::findChildrenFinished, [=](){ QCursor c; c.setShape(Qt::ArrowCursor); pv->setCursor(c); pv->sortByColumn(0, Qt::AscendingOrder); }); */ }); #endif } MainWindow::~MainWindow() { } peony/libpeony-qt/model/model-test/model-test.pro0000644000175000017500000000236114205101223021037 0ustar fengfeng#------------------------------------------------- # # Project created by QtCreator 2019-07-25T11:27:48 # #------------------------------------------------- QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = model-test TEMPLATE = app # The following define makes your compiler emit warnings if you use # any feature of Qt which has been marked as 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 you use 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 CONFIG += link_pkgconfig no_keywords c++11 PKGCONFIG += glib-2.0 gio-2.0 include(../../libpeony-qt.pri) SOURCES += \ main.cpp \ mainwindow.cpp HEADERS += \ mainwindow.h # Default rules for deployment. #qnx: target.path = /tmp/$${TARGET}/bin #else: unix:!android: target.path = /opt/$${TARGET}/bin #!isEmpty(target.path): INSTALLS += target peony/libpeony-qt/model/model-test/main.cpp0000644000175000017500000001033414205101223017667 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #include "mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "side-bar-model.h" #include "side-bar-proxy-filter-sort-model.h" #include "side-bar-abstract-item.h" #include #include int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; QToolBar t; QLineEdit e; Peony::PathBarModel pm; Peony::PathCompleter c; c.setModel(&pm); e.setCompleter(&c); w.addToolBar(Qt::TopToolBarArea, &t); t.addWidget(&e); QLabel l; l.setWordWrap(true); l.setMaximumWidth(480); l.setText("The file item model has provided the drag and drop interface." "you can drag the file item(s) to a folder, and the files will be " "moved into this folder. If you are interested about it and want " "to know how it works, you should see the source code of FileItemModel, " "FileOperationManager and FileMoveOperation." "\n\n" "The QListView is only accept items drag and drop in same class instances by default. " "If you want to drag and drop in different kinds of views, you should " "dirved the view class and override it drag and drop event handler for non-limit " "drag and drop event acception. Here I don't re-implement those handler."); w.setCentralWidget(&l); e.setText("network:///"); //e.setText("file://" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); e.connect(&e, &QLineEdit::returnPressed, [&]() { QListView *v = new QListView; /// set view attribute v->setAttribute(Qt::WA_DeleteOnClose); v->setWordWrap(true); v->setViewMode(QListView::IconMode); v->setGridSize(QSize(96, 96)); v->setResizeMode(QListView::Adjust); /// set select action v->setSelectionMode(QAbstractItemView::ExtendedSelection); /// set dnd actions v->setDragDropMode(QAbstractItemView::DragDrop); v->setDefaultDropAction(Qt::MoveAction); Peony::FileItemModel *model = new Peony::FileItemModel(v); model->setRootUri(e.text()); Peony::FileItemProxyFilterSortModel *proxy_model = new Peony::FileItemProxyFilterSortModel(model); proxy_model->setSourceModel(model); /// resort when load directory finished model->connect(model, &Peony::FileItemModel::findChildrenFinished, [=]() { proxy_model->sort(0); }); /// re-sort when file(s) moved in/out. model->connect(model, &Peony::FileItemModel::updated, [=]() { proxy_model->sort(0); }); /// double clicked for location change. v->setEditTriggers(QListView::EditKeyPressed); v->connect(v, &QListView::doubleClicked, [=](const QModelIndex &index) { auto item = proxy_model->itemFromIndex(index); if (item->hasChildren() || item->uri().startsWith("network:")) { model->setRootUri(item->uri()); } }); v->setModel(proxy_model); v->show(); }); w.show(); return a.exec(); } peony/libpeony-qt/model/side-bar-personal-item.h0000644000175000017500000000452414205115226020612 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef SIDEBARPERSONALITEM_H #define SIDEBARPERSONALITEM_H #include "peony-core_global.h" #include "side-bar-abstract-item.h" namespace Peony { class SideBarModel; class PEONYCORESHARED_EXPORT SideBarPersonalItem : public SideBarAbstractItem { Q_OBJECT public: explicit SideBarPersonalItem(QString uri, SideBarPersonalItem *parentItem, SideBarModel *model, QObject *parent = nullptr); Type type() override { return SideBarAbstractItem::PersonalItem; } QString uri() override; QString displayName(); QString iconName(); bool hasChildren() override { return m_is_root_child; } bool isRemoveable() override { return false; } bool isEjectable() override { return false; } bool isMountable() override { return false; } QModelIndex firstColumnIndex() override; QModelIndex lastColumnIndex() override; SideBarAbstractItem *parent() override { return m_parent; } public Q_SLOTS: void eject(GMountUnmountFlags ejectFlag) override {} void unmount() override {} void format() override {} void onUpdated() override {} void findChildren() override {} void findChildrenAsync() override {} void clearChildren() override {} private: SideBarPersonalItem *m_parent = nullptr; bool m_is_root_child = false; QString m_uri = nullptr; QString m_display_name = nullptr; QString m_icon_name = nullptr; }; } #endif // SIDEBARPERSONALITEM_H peony/libpeony-qt/model/model.pri0000644000175000017500000000233514205115226016010 0ustar fengfengINCLUDEPATH += $$PWD #include(../file-operation/file-operation.pri) HEADERS += \ $$PWD/file-item.h \ $$PWD/file-item-model.h \ $$PWD/file-item-proxy-filter-sort-model.h \ $$PWD/file-label-model.h \ $$PWD/side-bar-abstract-item.h \ $$PWD/side-bar-model.h \ $$PWD/side-bar-favorite-item.h \ $$PWD/side-bar-personal-item.h \ $$PWD/side-bar-file-system-item.h \ $$PWD/side-bar-proxy-filter-sort-model.h \ $$PWD/path-bar-model.h \ $$PWD/path-completer.h \ $$PWD/side-bar-separator-item.h \ $$PWD/side-bar-single-item.h \ $$PWD/side-bar-vfs-item.h \ $$PWD/side-bar-net-work-item.h SOURCES += \ $$PWD/file-item.cpp \ $$PWD/file-item-model.cpp \ $$PWD/file-item-proxy-filter-sort-model.cpp \ $$PWD/file-label-model.cpp \ $$PWD/side-bar-abstract-item.cpp \ $$PWD/side-bar-model.cpp \ $$PWD/side-bar-favorite-item.cpp \ $$PWD/side-bar-personal-item.cpp \ $$PWD/side-bar-file-system-item.cpp \ $$PWD/side-bar-proxy-filter-sort-model.cpp \ $$PWD/path-bar-model.cpp \ $$PWD/path-completer.cpp \ $$PWD/side-bar-separator-item.cpp \ $$PWD/side-bar-single-item.cpp \ $$PWD/side-bar-vfs-item.cpp \ $$PWD/side-bar-net-work-item.cpp peony/libpeony-qt/model/side-bar-file-system-item.cpp0000644000175000017500000005017614205115226021567 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "side-bar-file-system-item.h" #include "sync-thread.h" #include "file-info.h" #include "file-utils.h" #include "file-watcher.h" #include "file-info-job.h" //#include "volume-manager.h" #include "volumeManager.h" #include "side-bar-model.h" #include "file-enumerator.h" #include "gobject-template.h" #include "linux-pwd-helper.h" #include "side-bar-separator-item.h" #include #include #include #include #include #include #include using namespace Peony; SideBarFileSystemItem::SideBarFileSystemItem(QString uri, const Experimental_Peony::Volume& volume, SideBarFileSystemItem *parentItem, SideBarModel *model, QObject *parent) : SideBarAbstractItem (model, parent), /*m_uri(uri),*/m_volume(volume),m_parent(parentItem) { m_uri = uri; if(!volume.device().isEmpty()){ initVolumeInfo(volume); }else{ if(!parentItem){ initComputerInfo(); m_isRootChild = true; }else{ initDirInfo(uri); } } m_children = new QVector(); } SideBarFileSystemItem::~SideBarFileSystemItem() { if(m_enumerator) { delete m_enumerator; m_enumerator = nullptr; } } QString SideBarFileSystemItem::uri() { return FileUtils::urlEncode(m_uri); } QString SideBarFileSystemItem::displayName() { return m_displayName; } QModelIndex SideBarFileSystemItem::firstColumnIndex() { return m_model->firstColumnIndex(this); } QModelIndex SideBarFileSystemItem::lastColumnIndex() { return m_model->lastColumnIndex(this); } bool SideBarFileSystemItem::filterShowRow() { if (m_uri == "file:///data" && FileUtils::isFileExsit("file:///data/usershare")) { return false; } if (m_uri.startsWith("file:///home/")){ return false; } if (m_uri != "file:///") { QString gvfsDisplayName = m_mountPoint; QString gvfsUnixDevice = m_device; if (!gvfsUnixDevice.isNull() && (gvfsDisplayName.contains("DVD"))) return true; if(!gvfsUnixDevice.isNull() && !gvfsDisplayName.contains(":")) return false;//Filter some non-mountable drive items if (isMounted()) return true; if (isRemoveable() && isUnmountable()) { return true; } if (!isRemoveable() && !isEjectable() && !isStopable()) return true; return false; } return true; } void SideBarFileSystemItem::initDirInfo(const QString &uri) { if(uri.isEmpty()) return; m_children = nullptr; m_watcher = nullptr; m_iconName = "gtk-directory"; m_device = m_mountPoint = ""; m_uri = uri; auto info = FileInfo::fromUri(m_uri); m_displayName = info.get()->displayName(); m_mounted = m_ejectable = m_stopable = m_removeable = m_mountable = false; m_unmountable = false; } void SideBarFileSystemItem::initComputerInfo() { m_device = ""; m_mountPoint = ""; m_mounted = true; m_ejectable = false; m_mountable = false; m_removeable = false; m_unmountable= false; m_watcher = nullptr; m_uri = "computer:///"; m_iconName = ""; m_displayName = QObject::tr("Computer"); } void SideBarFileSystemItem::initVolumeInfo(const Experimental_Peony::Volume &volumeItem) { m_watcher = nullptr; m_iconName = volumeItem.icon(); m_device = volumeItem.device(); m_ejectable = volumeItem.canEject(); m_stopable = volumeItem.canStop(); m_unmountable = volumeItem.canUnmount();//可卸载 m_mountable = volumeItem.canMount(); //可挂载 m_removeable = m_ejectable? true:false; m_uri = m_mountPoint = volumeItem.mountPoint(); m_mounted = !m_uri.isEmpty(); m_displayName = volumeItem.name() + "(" + m_device + ")"; /* 手机和空光盘的m_uri需要额外设置 */ if(m_uri.isEmpty()){ if(m_device.startsWith("/dev/bus/usb")){ m_uri = "computer:///" + volumeItem.name() + ".volume";/* 手机(mtp、gphoto2) */ m_mounted = true; } else if(m_device.contains("/dev/sr") && (m_displayName.contains("DVD") ||m_displayName.contains("CD")))//更好的方法区分是否是空光盘? { m_mounted=true; m_unmountable = m_mountable=false; m_uri = "burn://"; } }else{ m_uri = "file://" + m_uri; } /* 文件系统项特殊处理 */ if("file:///"==m_uri){ m_unmountable = m_mountable = m_ejectable = m_stopable = false; m_mounted=true; m_displayName = QObject::tr("File System"); m_iconName="drive-harddisk-system-symbolic"; } } void SideBarFileSystemItem::clearChildren() { if(m_watcher) m_watcher->stopMonitor(); SideBarAbstractItem::clearChildren(); } void SideBarFileSystemItem::slot_volumeDeviceAdd(const Experimental_Peony::Volume &addItem) { qDebug()<<"add device:"<m_device == addItem.device()) { return; } } SideBarFileSystemItem *item = new SideBarFileSystemItem(nullptr, addItem, this, m_model); m_model->beginInsertRows(this->firstColumnIndex(), m_children->count(), m_children->count()); m_children->append(item); m_model->endInsertRows(); m_model->indexUpdated(this->firstColumnIndex()); m_model->dataChanged(item->firstColumnIndex(), item->lastColumnIndex()); } void SideBarFileSystemItem::slot_volumeDeviceRemove(const QString &removeDevice) { for (auto child : *m_children) { if (child->m_device == removeDevice|| (removeDevice == child->m_mountPoint/*gparted*/)) { int index = m_children->indexOf(child); m_model->beginRemoveRows(firstColumnIndex(), index, index); m_children->removeOne(child); m_model->endRemoveRows(); child->deleteLater(); break; } } m_model->indexUpdated(this->firstColumnIndex()); } void SideBarFileSystemItem::slot_volumeDeviceMount(const Experimental_Peony::Volume &volume) { QString device = volume.device(); QString mountPoint = volume.mountPoint(); if(mountPoint.isEmpty()) return; //更新model,元素信息更新 for(auto item:*m_children){ if(item->m_device == device){ item->m_mounted = true; /* 更新挂载状态 */ item->m_mountPoint = mountPoint; /* 设置挂载点,属性页会用到 */ item->m_mountable =volume.canMount(); item->m_unmountable = true; item->m_uri = "file://" + mountPoint;/* 更新uri,为了枚举操作 */ /* 手机和空光盘的m_uri需要额外设置 */ if(device.startsWith("/dev/bus/usb"))/* 手机设备(mtp、gphoto2)的uri */ item->m_uri = "computer:///" + volume.name() + ".volume"; else if(item->m_device.contains("/dev/sr") && (item->m_displayName.contains("DVD") ||item->m_displayName.contains("CD")))/* 空光盘 */ { item->m_uri="burn:///"; } m_model->dataChanged(item->firstColumnIndex(), item->lastColumnIndex()); break; } } } void SideBarFileSystemItem::slot_volumeDeviceUnmount(const QString &unmountDevice) { qDebug()<<__func__<<__LINE__<isEmpty()) return; //qDebug()<<__func__<<__LINE__<m_mountPoint == unmountDevice){/* 依靠挂载点属性匹配 */ item->m_uri = ""; /* 分区卸载后不可以做枚举操作 */ item->m_mountPoint = ""; /*挂载点置空,属性页会用到? */ item->m_mounted = false; /* 分区已卸载 */ item->m_unmountable = false; item->m_mountable = true; m_model->dataChanged(item->firstColumnIndex(), item->lastColumnIndex()); break; } } } void SideBarFileSystemItem::slot_volumeDeviceUpdate(const Experimental_Peony::Volume &updateDevice, QString property) { qDebug()<<__func__<<__LINE__; QString device; if(property != "name") return; device = updateDevice.device(); for(auto& item:*m_children){ if(item->m_device == device){ item->m_displayName = updateDevice.name() + "(" + device + ")"; //model更新 m_model->dataChanged(item->firstColumnIndex(), item->lastColumnIndex()); break; } } } void SideBarFileSystemItem::slot_fileCreate(const QString &uri) { //qDebug()<<"created:"<uri() == uri) { return; } } SideBarFileSystemItem *item = new SideBarFileSystemItem(uri, nullptr, this, m_model); m_model->beginInsertRows(this->firstColumnIndex(), m_children->count(), m_children->count()); m_children->append(item); m_model->endInsertRows(); m_model->dataChanged(item->firstColumnIndex(), item->lastColumnIndex()); } void SideBarFileSystemItem::slot_fileDelete(const QString &uri) { //qDebug()<<"deleted:"<uri() == uri) { int index = m_children->indexOf(child); m_model->beginRemoveRows(firstColumnIndex(), index, index); m_children->removeOne(child); m_model->endRemoveRows(); child->deleteLater(); break; } } m_model->indexUpdated(this->firstColumnIndex()); } void SideBarFileSystemItem::slot_fileRename(const QString &oldUri, const QString &newUri) { qDebug()<<"rename,old uri:"<uri() == oldUri){ item->m_uri = newUri; auto info = FileInfo::fromUri(item->m_uri); if (info->displayName().isEmpty()) { FileInfoJob j(info); j.querySync(); } item->m_displayName = info.get()->displayName(); m_model->dataChanged(item->firstColumnIndex(), item->lastColumnIndex()); } } } void SideBarFileSystemItem::slot_enumeratorPrepared(const std::shared_ptr &err, const QString &targetUri, bool critical) { connect(m_enumerator,&FileEnumerator::enumerateFinished,this, &SideBarFileSystemItem::slot_enumeratorFinish); m_enumerator->enumerateAsync(); } void SideBarFileSystemItem::slot_enumeratorFinish(bool successed) { if(!successed) return; auto infos = m_enumerator->getChildren(); bool isEmpty = true; int real_children_count = infos.count(); if (infos.isEmpty()) { auto separator = new SideBarSeparatorItem(SideBarSeparatorItem::EmptyFile, this, m_model); this->m_children->prepend(separator); m_model->insertRows(0, 1, this->firstColumnIndex()); return; } for (auto info: infos) { if (!info->displayName().startsWith(".") && (info->isDir() || info->isVolume())) { isEmpty = false; } auto targetUri = FileUtils::getTargetUri(info->uri()); //skip the independent files and remote server bool bRemoteServer=false; if(targetUri.startsWith("ftp://")||targetUri.startsWith("sftp://")||targetUri.startsWith("smb://")) bRemoteServer=true; if (!(info->isDir() || info->isVolume())||bRemoteServer) { real_children_count--; continue; } SideBarFileSystemItem *item = new SideBarFileSystemItem(info->uri(), nullptr, this, m_model); //check is mounted. //FIXME: replace BLOCKING api in ui thread. bool isUmountable = FileUtils::isFileUnmountable(info->uri()); item->m_mounted = (!targetUri.isEmpty() && (targetUri != "file:///")) || isUmountable; m_children->append(item); } m_model->insertRows(0, real_children_count, firstColumnIndex()); if (isEmpty) { auto separator = new SideBarSeparatorItem(SideBarSeparatorItem::EmptyFile, this, m_model); this->m_children->prepend(separator); m_model->insertRows(0, 1, this->firstColumnIndex()); } } /*! * \brief SideBarFileSystemItem::findChildren * \bug root doesn't support gvfs, so computer:/// cannot be enumerated. * to avoid the bug, I forbided find filesystem item children in root. * I should use another way to display the devices/volumes. */ void SideBarFileSystemItem::findChildren() { clearChildren(); if (m_parent == nullptr) { int volumeCount = 0; QList* volumeList; volumeList = Experimental_Peony::VolumeManager::getInstance()->allVaildVolumes(); volumeCount = volumeList->count(); for(int i=0; iat(i); m_model->beginInsertRows(this->firstColumnIndex(), m_children->count(), m_children->count()); SideBarFileSystemItem* item = new SideBarFileSystemItem(volume.name(), volume, this, m_model); m_children->append(item); m_model->endInsertRows(); m_model->indexUpdated(this->lastColumnIndex()); m_model->dataChanged(item->firstColumnIndex(), item->lastColumnIndex()); } }else{ //对挂载点进行已存在文件的枚举操作 QString enumdir = m_uri; if(m_uri.startsWith("computer:///")){//GFileEnumerator不识别computer:///,只识别file:/// auto info = FileInfo::fromUri(m_uri); FileInfoJob job(info); job.querySync(); enumdir = info.get()->targetUri(); } if(!m_enumerator) m_enumerator= new FileEnumerator(); m_enumerator->setEnumerateDirectory(enumdir); m_enumerator->setEnumerateWithInfoJob(); connect(m_enumerator,&FileEnumerator::prepared,this,&SideBarFileSystemItem::slot_enumeratorPrepared); m_enumerator->prepare(); } /* 设备动态增减处理 */ if("computer:///" == m_uri){ auto volumeManager = Experimental_Peony::VolumeManager::getInstance(); connect(volumeManager,&Experimental_Peony::VolumeManager::volumeAdd,this,&SideBarFileSystemItem::slot_volumeDeviceAdd); connect(volumeManager,&Experimental_Peony::VolumeManager::volumeRemove,this,&SideBarFileSystemItem::slot_volumeDeviceRemove); connect(volumeManager,&Experimental_Peony::VolumeManager::mountAdd,this,&SideBarFileSystemItem::slot_volumeDeviceMount); connect(volumeManager,&Experimental_Peony::VolumeManager::mountRemove,this,&SideBarFileSystemItem::slot_volumeDeviceUnmount); connect(volumeManager,&Experimental_Peony::VolumeManager::volumeUpdate,this,&SideBarFileSystemItem::slot_volumeDeviceUpdate); }else{ /* 对挂载目录监听 */ if(!m_watcher) m_watcher = std::make_shared(m_uri, nullptr, true); m_watcher->setMonitorChildrenChange(); connect(m_watcher.get(),&FileWatcher::fileCreated,this,&SideBarFileSystemItem::slot_fileCreate); connect(m_watcher.get(),&FileWatcher::fileDeleted,this,&SideBarFileSystemItem::slot_fileDelete); connect(m_watcher.get(),&FileWatcher::fileRenamed,this,&SideBarFileSystemItem::slot_fileRename); m_watcher->startMonitor(); } } void SideBarFileSystemItem::findChildrenAsync() { //TODO add async method. findChildren(); } bool SideBarFileSystemItem::isRemoveable() { return m_removeable; } bool SideBarFileSystemItem::isEjectable() { return m_ejectable; } bool SideBarFileSystemItem::isMountable() { return m_mountable; } bool SideBarFileSystemItem::isUnmountable() { return m_unmountable; } bool SideBarFileSystemItem::isMounted() { return m_mounted; } void SideBarFileSystemItem::unmount() { SyncThread *syncThread = new SyncThread(m_uri); QThread* currentThread = new QThread(); syncThread->moveToThread(currentThread); connect(currentThread,&QThread::started,syncThread,&SyncThread::parentStartedSlot); connect(syncThread,&SyncThread::syncFinished,this,[=](){ m_volume.unmount(); syncThread->disconnect(this); syncThread->deleteLater(); currentThread->disconnect(SIGNAL(started())); //currentThread->deleteLater(); }); currentThread->start(); } void SideBarFileSystemItem::eject(GMountUnmountFlags ejectFlag) { SyncThread *syncThread = new SyncThread(m_uri); QThread* currentThread = new QThread(); syncThread->moveToThread(currentThread); connect(currentThread,&QThread::started,syncThread,&SyncThread::parentStartedSlot); connect(syncThread,&SyncThread::syncFinished,this,[=](){ m_volume.eject(ejectFlag); syncThread->disconnect(this); syncThread->deleteLater(); currentThread->disconnect(SIGNAL(started())); //currentThread->deleteLater(); }); currentThread->start(); } static UDisksObject *get_object_from_block_device (UDisksClient *client,const gchar *block_device) { struct stat statbuf; const gchar *crypto_backing_device; UDisksObject *object, *crypto_backing_object; UDisksBlock *block; object = NULL; if (stat (block_device, &statbuf) != 0) { return object; } block = udisks_client_get_block_for_dev (client, statbuf.st_rdev); if (block == NULL) { return object; } object = UDISKS_OBJECT (g_dbus_interface_dup_object (G_DBUS_INTERFACE (block))); g_object_unref (block); crypto_backing_device = udisks_block_get_crypto_backing_device ((udisks_object_peek_block (object))); crypto_backing_object = udisks_client_get_object (client, crypto_backing_device); if (crypto_backing_object != NULL) { g_object_unref (object); object = crypto_backing_object; } return object; } void SideBarFileSystemItem::ejectOrUnmount() { if (isRemoveable()) eject(G_MOUNT_UNMOUNT_NONE); else if (isUnmountable()) unmount(); } void SideBarFileSystemItem::mount() { if(isMountable()) m_volume.mount(); } //update udisk file info void SideBarFileSystemItem::updateFileInfo(SideBarFileSystemItem *pThis){ //FIXME: replace BLOCKING api in ui thread. auto fileInfo = FileInfo::fromUri(pThis->m_uri); FileInfoJob fileJob(fileInfo); fileJob.querySync(); QString tmpName = FileUtils::getFileDisplayName(pThis->m_uri); //old's drive name -> now's volume name. fix #17968 FileUtils::queryVolumeInfo(pThis->m_uri,pThis->m_volume_name,pThis->m_unix_device,tmpName); //icon name. pThis->m_iconName = FileUtils::getFileIconName(pThis->m_uri,false); } peony/libpeony-qt/model/side-bar-personal-item.cpp0000644000175000017500000000754714205101223021145 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "side-bar-personal-item.h" #include "side-bar-model.h" #include "file-utils.h" #include "file-info.h" #include "file-info-job.h" #include using namespace Peony; SideBarPersonalItem::SideBarPersonalItem(QString uri, SideBarPersonalItem *parentItem, SideBarModel *model, QObject *parent) : SideBarAbstractItem (model, parent) { m_parent = parentItem; m_is_root_child = parentItem == nullptr; if (m_is_root_child) { QString homeUri = "file://" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation); m_uri = homeUri; m_display_name = tr("Personal"); //m_icon_name = "emblem-personal"; //top dir don't show icon m_icon_name = ""; QString documentUri = "file://" + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); SideBarPersonalItem *documentItem = new SideBarPersonalItem(documentUri, this, m_model); m_children->append(documentItem); QString pictureUri = "file://" + QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); SideBarPersonalItem *pictureItem = new SideBarPersonalItem(pictureUri, this, m_model); m_children->append(pictureItem); QString mediaUri = "file://" + QStandardPaths::writableLocation(QStandardPaths::MoviesLocation); SideBarPersonalItem *mediaItem = new SideBarPersonalItem(mediaUri, this, m_model); m_children->append(mediaItem); QString downloadUri = "file://" + QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); SideBarPersonalItem *downloadItem = new SideBarPersonalItem(downloadUri, this, m_model); m_children->append(downloadItem); QString musicUri = "file://" + QStandardPaths::writableLocation(QStandardPaths::MusicLocation); SideBarPersonalItem *musicItem = new SideBarPersonalItem(musicUri, this, m_model); m_children->append(musicItem); m_model->insertRows(0, 5, firstColumnIndex()); return; } m_uri = uri; //FIXME: replace BLOCKING api in ui thread. m_display_name = FileUtils::getFileDisplayName(uri); m_icon_name = FileUtils::getFileIconName(uri); m_info = FileInfo::fromUri(uri); auto infoJob = new FileInfoJob(m_info); infoJob->setAutoDelete(); connect(infoJob, &FileInfoJob::queryAsyncFinished, this, [=](){ Q_EMIT this->queryInfoFinished(); }); infoJob->queryAsync(); } QString SideBarPersonalItem::uri() { return m_uri; } QString SideBarPersonalItem::displayName() { if (!m_info) return m_display_name; return m_info.get()->displayName(); } QString SideBarPersonalItem::iconName() { if (!m_info) return m_icon_name; return m_info.get()->iconName(); } QModelIndex SideBarPersonalItem::firstColumnIndex() { return m_model->firstColumnIndex(this); } QModelIndex SideBarPersonalItem::lastColumnIndex() { return m_model->lastColumnIndex(this); } peony/libpeony-qt/model/side-bar-separator-item.cpp0000644000175000017500000000245014205101223021306 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "side-bar-separator-item.h" #include "side-bar-model.h" using namespace Peony; SideBarSeparatorItem::SideBarSeparatorItem(Details type, SideBarAbstractItem *parentItem, SideBarModel *model, QObject *parent) : SideBarAbstractItem(model, parent), m_type(type), m_parent(parentItem) { } QModelIndex SideBarSeparatorItem::firstColumnIndex() { return m_model->firstColumnIndex(this); } QModelIndex SideBarSeparatorItem::lastColumnIndex() { return m_model->lastColumnIndex(this); } peony/libpeony-qt/model/side-bar-model.h0000644000175000017500000000715314205115226017134 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef SIDEBARMODEL_H #define SIDEBARMODEL_H #include #include #include "peony-core_global.h" namespace Peony { class SideBarAbstractItem; /*! * \brief The SideBarModel class * \todo * Add dnd support and custom favorite items support. */ class PEONYCORESHARED_EXPORT SideBarModel : public QAbstractItemModel { friend class SideBarFileSystemItem; friend class SideBarAbstractItem; friend class SideBarFavoriteItem; friend class SideBarNetWorkItem; Q_OBJECT public: explicit SideBarModel(QObject *parent = nullptr); ~SideBarModel() override; QModelIndex firstColumnIndex(SideBarAbstractItem *item); QModelIndex lastColumnIndex(SideBarAbstractItem *item); SideBarAbstractItem *itemFromIndex(const QModelIndex &index); // Header: QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole) override; // Basic functionality: QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; // Fetch data dynamically: bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; bool canFetchMore(const QModelIndex &parent) const override; void fetchMore(const QModelIndex &parent) override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; // Editable: bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex& index) const override; // Add data: bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex()) override; // Remove data: bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex()) 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; Q_SIGNALS: void indexUpdated(const QModelIndex &index); protected: QVector *m_root_children = nullptr; void onIndexUpdated(const QModelIndex &index); protected: QStringList m_bookmark_uris; }; } #endif // SIDEBARMODEL_H peony/libpeony-qt/model/side-bar-file-system-item.h0000644000175000017500000001015414205101223021214 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef SIDEBARFILESYSTEMITEM_H #define SIDEBARFILESYSTEMITEM_H #include "peony-core_global.h" #include "side-bar-abstract-item.h" #include #include #include "volumeManager.h" #include "gerror-wrapper.h" namespace Peony { class FileWatcher; class FileEnumerator; class FileUtils; class PEONYCORESHARED_EXPORT SideBarFileSystemItem : public SideBarAbstractItem { Q_OBJECT public: explicit SideBarFileSystemItem(QString uri,const Experimental_Peony::Volume& volume, SideBarFileSystemItem *parentItem, SideBarModel *model, QObject *parent = nullptr); ~SideBarFileSystemItem(); Type type() override { return SideBarAbstractItem::FileSystemItem; } QString uri() override; QString displayName() override; QString iconName() override { return m_iconName; } bool hasChildren() override { return true; } bool isRemoveable() override; bool isEjectable() override; bool isMountable() override; bool isUnmountable() override; //TODO: monitoring the mount state bool isMounted() override; QModelIndex firstColumnIndex() override; QModelIndex lastColumnIndex() override; SideBarAbstractItem *parent() override { return m_parent; } Experimental_Peony::Volume getVolume(){ return m_volume; } bool filterShowRow(); private: void initDirInfo(const QString& uri); //普通目录 void initComputerInfo(); //计算机Computer void initVolumeInfo(const Experimental_Peony::Volume& volumeItem); //分区设备 public Q_SLOTS: void eject(GMountUnmountFlags ejectFlag) override; void unmount() override; void ejectOrUnmount() override; void mount()override; void format() override {} void onUpdated() override {} void findChildren() override; void findChildrenAsync() override; void clearChildren() override; void slot_volumeDeviceAdd(const Experimental_Peony::Volume& addItem);/*设备增加:插入、关闭gparted */ void slot_volumeDeviceRemove(const QString& removeDevice);/*设备移除需要匹配device或者mountPoint属性,移除:弹出、拔出、打开gparted */ void slot_volumeDeviceMount(const Experimental_Peony::Volume& volume);/*设备挂载 */ void slot_volumeDeviceUnmount(const QString& unmountDevice);/*设备卸载 */ void slot_volumeDeviceUpdate(const Experimental_Peony::Volume& updateDevice,QString property);/*设备的属性更新:如重设卷标*/ void slot_fileCreate(const QString& uri); void slot_fileDelete(const QString& uri); void slot_fileRename(const QString &oldUri, const QString &newUri); void slot_enumeratorPrepared(const std::shared_ptr& err, const QString& targetUri, bool critical); void slot_enumeratorFinish(bool successed); protected: void updateFileInfo(SideBarFileSystemItem *pThis); private: SideBarFileSystemItem *m_parent = nullptr; Experimental_Peony::Volume m_volume = nullptr; std::shared_ptr m_watcher = nullptr; FileEnumerator *m_enumerator = nullptr; bool m_isRootChild = false; QString m_unix_device; // sdb1, etc... QString m_volume_name; // Windows, Data etc... }; } #endif // SIDEBARFILESYSTEMITEM_H peony/libpeony-qt/model/file-label-model.cpp0000644000175000017500000003133114205115226017770 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #include "file-label-model.h" #include "file-meta-info.h" #include "file-info.h" #include "audio-play-manager.h" #include static FileLabelModel *global_instance = nullptr; FileLabelModel::FileLabelModel(QObject *parent) : QAbstractListModel(parent) { m_label_settings = new QSettings(QSettings::UserScope, "org.ukui", "peony-qt", this); if (m_label_settings->value("lastid").isNull()) { //adjsut color value to design instead of Qt define color,task#25507 QColor Red(0xFA6056); QColor Orange(0xF8A34C); QColor Yellow(0xF7CE52); QColor Green(0x5FD065); QColor Blue(0x478EF8); QColor Purple(0xB470D5); QColor Gray(0x9D9DA0); //init settings addLabel(tr("Red"), Red); addLabel(tr("Orange"), Orange); addLabel(tr("Yellow"), Yellow); addLabel(tr("Green"), Green); addLabel(tr("Blue"), Blue); addLabel(tr("Purple"), Purple); addLabel(tr("Gray"), Gray); //addLabel(tr("Transparent"), Qt::transparent); } else { initLabelItems(); } } FileLabelModel::~FileLabelModel() { } FileLabelModel *FileLabelModel::getGlobalModel() { if (!global_instance) { global_instance = new FileLabelModel; } return global_instance; } const QStringList FileLabelModel::getLabels() { QStringList l; int size = m_label_settings->beginReadArray("labels"); for (int i = 0; i < size; i++) { m_label_settings->setArrayIndex(i); if (m_label_settings->value("visible").toBool()) { l<value("label").toString(); } } m_label_settings->endArray(); return l; } const QList FileLabelModel::getColors() { QList l; int size = m_label_settings->beginReadArray("labels"); for (int i = 0; i < size; i++) { m_label_settings->setArrayIndex(i); if (m_label_settings->value("visible").toBool()) { l<(m_label_settings->value("color")); } } m_label_settings->endArray(); return l; } int FileLabelModel::lastLabelId() { if (m_label_settings->value("lastid").isNull()) { return 0; } else { return m_label_settings->value("lastid").toInt(); } } void FileLabelModel::addLabel(const QString &label, const QColor &color) { beginResetModel(); if (getLabels().contains(label) || getColors().contains(color)) { Peony::AudioPlayManager::getInstance()->playWarningAudio(); QMessageBox::critical(nullptr, tr("Error"), tr("Label or color is duplicated.")); return; } int lastid = lastLabelId(); m_label_settings->beginWriteArray("labels"); m_label_settings->setArrayIndex(lastid + 1); m_label_settings->setValue("label", label); m_label_settings->setValue("color", color); m_label_settings->setValue("visible", true); m_label_settings->endArray(); auto item = new FileLabelItem(this); item->m_id = lastid + 1; item->m_name = label; item->m_color = color; m_labels.append(item); addId(); connect(item, &FileLabelItem::nameChanged, this, [=](const QString &name) { m_label_settings->beginWriteArray("labels"); m_label_settings->setArrayIndex(item->id()); m_label_settings->setValue("label", name); m_label_settings->endArray(); m_label_settings->sync(); }); connect(item, &FileLabelItem::colorChanged, this, [=](const QColor &color) { m_label_settings->beginWriteArray("labels"); m_label_settings->setArrayIndex(item->id()); m_label_settings->setValue("color", color); m_label_settings->endArray(); m_label_settings->sync(); }); endResetModel(); } void FileLabelModel::removeLabel(int id) { beginResetModel(); for (auto item : m_labels) { if (item->id() == id) { m_labels.removeOne(item); item->deleteLater(); break; } } m_label_settings->beginWriteArray("labels", lastLabelId() + 1); m_label_settings->setArrayIndex(id); m_label_settings->setValue("visible", false); m_label_settings->endArray(); m_label_settings->sync(); Q_EMIT dataChanged(QModelIndex(), QModelIndex()); endResetModel(); } void FileLabelModel::setLabelName(int id, const QString &name) { for (auto item : m_labels) { if (item->id() == id) { item->setName(name); int row = m_labels.indexOf(item); Q_EMIT dataChanged(index(row), index(row)); break; } } } void FileLabelModel::setLabelColor(int id, const QColor &color) { for (auto item : m_labels) { if (item->id() == id) { item->setColor(color); int row = m_labels.indexOf(item); Q_EMIT dataChanged(index(row), index(row)); break; } } } const QList FileLabelModel::getFileLabelIds(const QString &uri) { QList l; auto metaInfo = Peony::FileMetaInfo::fromUri(uri); if (! metaInfo || metaInfo->getMetaInfoVariant(PEONY_FILE_LABEL_IDS).isNull()) return l; auto labels = metaInfo->getMetaInfoStringList(PEONY_FILE_LABEL_IDS); for (auto label : labels) { l<getMetaInfoVariant(PEONY_FILE_LABEL_IDS).isNull()) return l; auto labels = metaInfo->getMetaInfoStringList(PEONY_FILE_LABEL_IDS); for (auto label : labels) { auto id = label.toInt(); auto item = itemFromId(id); if (item) { l<name(); } } return l; } const QList FileLabelModel::getFileColors(const QString &uri) { QList l; auto metaInfo = Peony::FileMetaInfo::fromUri(uri); if (! metaInfo || metaInfo->getMetaInfoVariant(PEONY_FILE_LABEL_IDS).isNull()) return l; auto labels = metaInfo->getMetaInfoStringList(PEONY_FILE_LABEL_IDS); for (auto label : labels) { auto id = label.toInt(); auto item = itemFromId(id); if (item) { l<color(); } } return l; } FileLabelItem *FileLabelModel::itemFromId(int id) { for (auto item : this->m_labels) { if (id == item->id()) { return item; } } return nullptr; } FileLabelItem *FileLabelModel::itemFormIndex(const QModelIndex &index) { if (index.isValid()) { return m_labels.at(index.row()); } return nullptr; } QList FileLabelModel::getAllFileLabelItems() { return m_labels; } void FileLabelModel::addLabelToFile(const QString &uri, int labelId) { auto metaInfo = Peony::FileMetaInfo::fromUri(uri); QStringList labelIds; if (metaInfo && !metaInfo->getMetaInfoVariant(PEONY_FILE_LABEL_IDS).isNull()) labelIds = metaInfo->getMetaInfoStringList(PEONY_FILE_LABEL_IDS); labelIds<setMetaInfoStringList(PEONY_FILE_LABEL_IDS, labelIds); Q_EMIT fileLabelChanged(uri); } void FileLabelModel::removeFileLabel(const QString &uri, int labelId) { auto metaInfo = Peony::FileMetaInfo::fromUri(uri); if (! metaInfo) return; if (labelId <= 0) { metaInfo->removeMetaInfo(PEONY_FILE_LABEL_IDS); } else { if (metaInfo->getMetaInfoVariant(PEONY_FILE_LABEL_IDS).isNull()) return; QStringList labelIds = metaInfo->getMetaInfoStringList(PEONY_FILE_LABEL_IDS); labelIds.removeOne(QString::number(labelId)); metaInfo->setMetaInfoStringList(PEONY_FILE_LABEL_IDS, labelIds); } Q_EMIT fileLabelChanged(uri); } int FileLabelModel::rowCount(const QModelIndex &parent) const { // For list models only the root node (an invalid parent) should return the list's size. For all // other (valid) parents, rowCount() should return 0 so that it does not become a tree model. if (parent.isValid()) return 0; // FIXME: Implement me! return m_labels.size(); } QVariant FileLabelModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); // FIXME: Implement me! switch (role) { case Qt::DisplayRole: { return m_labels.at(index.row())->name(); } case Qt::DecorationRole: { return m_labels.at(index.row())->color(); } default: return QVariant(); } } bool FileLabelModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (data(index, role) != value) { // FIXME: Implement me! auto name = value.toString(); if (name.isEmpty()) { return false; } if (getLabels().contains(name)) { Peony::AudioPlayManager::getInstance()->playWarningAudio(); QMessageBox::critical(nullptr, tr("Error"), tr("Label or color is duplicated.")); return false; } this->setLabelName(m_labels.at(index.row())->id(), name); Q_EMIT dataChanged(index, index, QVector() << role); return true; } return false; } Qt::ItemFlags FileLabelModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; // FIXME: Implement me! } bool FileLabelModel::insertRows(int row, int count, const QModelIndex &parent) { beginInsertRows(parent, row, row + count - 1); // FIXME: Implement me! endInsertRows(); return true; } bool FileLabelModel::removeRows(int row, int count, const QModelIndex &parent) { beginRemoveRows(parent, row, row + count - 1); // FIXME: Implement me! endRemoveRows(); return true; } void FileLabelModel::setName(FileLabelItem *item, const QString &name) { m_label_settings->beginWriteArray("labels", lastLabelId() + 1); m_label_settings->setArrayIndex(item->id()); m_label_settings->setValue("label", name); m_label_settings->endArray(); m_label_settings->sync(); } void FileLabelModel::setColor(FileLabelItem *item, const QColor &color) { m_label_settings->beginWriteArray("labels", lastLabelId() + 1); m_label_settings->setArrayIndex(item->id()); m_label_settings->setValue("color", color); m_label_settings->endArray(); m_label_settings->sync(); } void FileLabelModel::initLabelItems() { beginResetModel(); auto size = m_label_settings->beginReadArray("labels"); for (int i = 0; i < size; i++) { m_label_settings->setArrayIndex(i); bool visible = m_label_settings->value("visible").toBool(); if (visible) { auto name = m_label_settings->value("label").toString(); auto color = qvariant_cast(m_label_settings->value("color")); auto item = new FileLabelItem(this); item->m_id = i; item->m_name = name; item->m_color = color; m_labels.append(item); } } m_label_settings->endArray(); endResetModel(); } void FileLabelModel::addId() { int lastid = lastLabelId(); m_label_settings->setValue("lastid", lastid + 1); m_label_settings->sync(); } //FileLabelItem FileLabelItem::FileLabelItem(QObject *parent) { //should be initialized in model. } int FileLabelItem::id() { return m_id; } const QString FileLabelItem::name() { return m_name; } const QColor FileLabelItem::color() { return m_color; } void FileLabelItem::setName(const QString &name) { m_name = name; if (m_id >= 0) { if (global_instance) global_instance->setName(this, name); } } void FileLabelItem::setColor(const QColor &color) { if (color.blackF() == 1) { auto black = color; black.setRgbF(0.01, 0.01, 0.01); setColor(black); return; } m_color = color; if (m_id >= 0) { if (global_instance) global_instance->setColor(this, color); } } peony/libpeony-qt/model/side-bar-proxy-filter-sort-model.cpp0000644000175000017500000000727214205115226023120 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "side-bar-proxy-filter-sort-model.h" #include "side-bar-abstract-item.h" #include "side-bar-model.h" #include "file-utils.h" #include "file-info.h" #include "file-info-job.h" #include #include #include using namespace Peony; SideBarProxyFilterSortModel::SideBarProxyFilterSortModel(QObject *parent) : QSortFilterProxyModel(parent) { setDynamicSortFilter(true); m_locale = QLocale(QLocale::system().name()); m_comparer = QCollator(m_locale); m_comparer.setNumericMode(true); } bool SideBarProxyFilterSortModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { auto index = sourceModel()->index(sourceRow, 0, sourceParent); auto item = static_cast(index.internalPointer()); if (item->type() != SideBarAbstractItem::SeparatorItem) { if (item->displayName().isNull() && item->type() == SideBarAbstractItem::FileSystemItem) return false; //not exist path filter if (item->type() == SideBarAbstractItem::FavoriteItem && ! item->uri().isEmpty()) { QDir dir(QUrl(item->uri()).path()); if (! dir.exists()) return false; } } if (item->type() == SideBarAbstractItem::NetWorkItem) { if (item->uri().isEmpty()) { return false; } } //comment to fix bug 41426, user add .config file to bookmark for convinient accesss // if (item) { // if (!item->displayName().isEmpty()) { // if (QString(item->displayName().at(0)) == ".") { // return false; // } // } // } if (item->type() == SideBarAbstractItem::FileSystemItem) { if (sourceParent.data(Qt::UserRole).toString() == "computer:///") { item->filterShowRow(); } } return true; } bool SideBarProxyFilterSortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { //qDebug()<<"less than"; /* 第一层不排序 */ if(!left.parent().isValid() && !right.parent().isValid()) return sortOrder() == Qt::AscendingOrder? true: false; if (!(left.isValid() && right.isValid())) { return QSortFilterProxyModel::lessThan(left, right); } auto leftItem = static_cast(left.internalPointer()); auto rightItem = static_cast(right.internalPointer()); if (leftItem->type() != SideBarAbstractItem::FileSystemItem || rightItem->type() != SideBarAbstractItem::FileSystemItem) { return false; } //qDebug()<displayName()<displayName(); return m_comparer.compare(leftItem->displayName(), rightItem->displayName()) > 0; } SideBarAbstractItem *SideBarProxyFilterSortModel::itemFromIndex(const QModelIndex &proxy_index) { SideBarModel *model = static_cast(sourceModel()); auto index = mapToSource(proxy_index); return model->itemFromIndex(index); } peony/libpeony-qt/model/file-label-model.h0000644000175000017500000000670214205101223017431 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #ifndef FILELABELMODEL_H #define FILELABELMODEL_H #include #include #include #include #define PEONY_FILE_LABEL_IDS "peony-file-label-ids" class FileLabelItem; class PEONYCORESHARED_EXPORT FileLabelModel : public QAbstractListModel { Q_OBJECT public: static FileLabelModel *getGlobalModel(); const QStringList getLabels(); const QList getColors(); int lastLabelId(); void addLabel(const QString &label, const QColor &color); void removeLabel(int id); void setLabelName(int id, const QString &name); void setLabelColor(int id, const QColor &color); void addLabelToFile(const QString &uri, int labelId); void removeFileLabel(const QString &uri, int labelId = -1); const QList getFileLabelIds(const QString &uri); const QStringList getFileLabels(const QString &uri); const QList getFileColors(const QString &uri); FileLabelItem *itemFromId(int id); FileLabelItem *itemFormIndex(const QModelIndex &index); QList getAllFileLabelItems(); // Basic functionality: int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; // Editable: bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex& index) const override; // Add data: bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; // Remove data: bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; Q_SIGNALS: void fileLabelChanged(const QString &uri); public Q_SLOTS: void setName(FileLabelItem *item, const QString &name); void setColor(FileLabelItem *item, const QColor &color); protected: void initLabelItems(); void addId(); private: explicit FileLabelModel(QObject *parent = nullptr); ~FileLabelModel(); QSettings *m_label_settings; QList m_labels; }; class PEONYCORESHARED_EXPORT FileLabelItem : public QObject { friend class FileLabelModel; Q_OBJECT public: explicit FileLabelItem(QObject *parent = nullptr); int id(); const QString name(); const QColor color(); void setName(const QString &name); void setColor(const QColor &color); Q_SIGNALS: void nameChanged(const QString &name); void colorChanged(const QColor &color); private: int m_id = -1; //invalid QString m_name = nullptr; QColor m_color = Qt::transparent; }; #endif // FILELABELMODEL_H peony/libpeony-qt/model/side-bar-model.cpp0000644000175000017500000003264514205115226017473 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "side-bar-model.h" #include "side-bar-favorite-item.h" #include "side-bar-personal-item.h" #include "side-bar-file-system-item.h" #include "side-bar-separator-item.h" #include "side-bar-net-work-item.h" #include "file-info.h" #include "file-info-job.h" #include "bookmark-manager.h" #include "file-operation-utils.h" #include "vfs-plugin-manager.h" #include "side-bar-vfs-item.h" #include "side-bar-single-item.h" #include "file-utils.h" #include #include #include #include using namespace Peony; SideBarModel::SideBarModel(QObject *parent) : QAbstractItemModel(parent) { beginResetModel(); m_root_children = new QVector(); auto vfsMgr = VFSPluginManager::getInstance(); auto plugins = vfsMgr->registeredPlugins(); for (auto plugin : plugins) { if (plugin->uriScheme().contains("kydroid://") || plugin->uriScheme().contains("kmre://")) { continue; } if (plugin->holdInSideBar()) { m_root_children->append(new SideBarVFSItem(plugin, this)); } } // SideBarSeparatorItem *separator1 = new SideBarSeparatorItem(SideBarSeparatorItem::Large, nullptr, this, this); // m_root_children->append(separator1); SideBarFavoriteItem *favorite_root_item = new SideBarFavoriteItem(nullptr, nullptr, this); m_root_children->append(favorite_root_item); //favorite_root_item->findChildren(); // SideBarSeparatorItem *separator2 = new SideBarSeparatorItem(SideBarSeparatorItem::Small, nullptr, this, this); // m_root_children->append(separator2); // if (FileUtils::isFileExsit("file:///data/usershare")) { // SideBarSingleItem *userShareItem = new SideBarSingleItem("file:///data/usershare", nullptr, tr("Shared Data"), this); // m_root_children->append(userShareItem); // } // SideBarPersonalItem *personal_root_item = new SideBarPersonalItem(nullptr, nullptr, this); // m_root_children->append(personal_root_item); //personal_root_item->findChildren(); // SideBarSeparatorItem *separator3 = new SideBarSeparatorItem(SideBarSeparatorItem::Small, nullptr, this, this); // m_root_children->append(separator3); SideBarFileSystemItem *computerItem = new SideBarFileSystemItem(nullptr,nullptr, nullptr, this); m_root_children->append(computerItem); //computerItem->findChildren(); // SideBarSingleItem *networkItem = new SideBarSingleItem("network:///", "network-workgroup-symbolic", tr("Network"), this); SideBarNetWorkItem *networkItem = new SideBarNetWorkItem("network:///", "network-workgroup-symbolic", tr("Network"), nullptr, this); m_root_children->append(networkItem); endResetModel(); //empty-file separator connect(this, &SideBarModel::indexUpdated, this, &SideBarModel::onIndexUpdated); } SideBarModel::~SideBarModel() { for (auto child : *m_root_children) { delete child; } m_root_children->clear(); delete m_root_children; } QModelIndex SideBarModel::firstColumnIndex(SideBarAbstractItem *item) { if (item->parent() != nullptr) { return createIndex(item->parent()->m_children->indexOf(item), 0, item); } else { return createIndex(m_root_children->indexOf(item), 0, item); } } QModelIndex SideBarModel::lastColumnIndex(SideBarAbstractItem *item) { if (item->parent() != nullptr) { createIndex(item->parent()->m_children->indexOf(item), 1, item); } else { for (auto child : *m_root_children) { if (item->type() == child->type()) { return createIndex(m_root_children->indexOf(child), 1, item); } } } return QModelIndex(); } SideBarAbstractItem *SideBarModel::itemFromIndex(const QModelIndex &index) { return static_cast(index.internalPointer()); } QVariant SideBarModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(section); Q_UNUSED(orientation); Q_UNUSED(role); return QVariant(); } bool SideBarModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) { return false; } QModelIndex SideBarModel::index(int row, int column, const QModelIndex &parent) const { if (!parent.isValid()) { return createIndex(row, column, m_root_children->at(row)); } SideBarAbstractItem *parentItem = static_cast(parent.internalPointer()); if (parentItem->m_children->count() > row) { return createIndex(row, column, parentItem->m_children->at(row)); } return QModelIndex(); } QModelIndex SideBarModel::parent(const QModelIndex &index) const { SideBarAbstractItem *item = static_cast(index.internalPointer()); //qDebug()<uri(); if (!item) return QModelIndex(); if (item->parent()) return item->parent()->firstColumnIndex(); return QModelIndex(); } int SideBarModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) return m_root_children->count(); SideBarAbstractItem *parentItem = static_cast(parent.internalPointer()); //qDebug()<uri(); return parentItem->m_children->count(); } int SideBarModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 2; } bool SideBarModel::hasChildren(const QModelIndex &parent) const { if (!parent.isValid()) return true; SideBarAbstractItem *parentItem = static_cast(parent.internalPointer()); return parentItem->hasChildren(); } bool SideBarModel::canFetchMore(const QModelIndex &parent) const { Q_UNUSED(parent); return true; } void SideBarModel::fetchMore(const QModelIndex &parent) { Q_UNUSED(parent); } QVariant SideBarModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); SideBarAbstractItem *item = static_cast(index.internalPointer()); if (index.column() == 0 && !index.parent().isValid()) { if(role == Qt::DecorationRole) { return QVariant(); } } if (index.column() == 1) { if(role == Qt::DecorationRole){ bool unmountAble,ejectAble; unmountAble = item->isUnmountable(); ejectAble = item->isEjectable()||item->isStopable(); if(unmountAble || ejectAble){ if(item->isMounted()) return QVariant(QIcon::fromTheme("media-eject-symbolic")); else return QVariant(); }else return QVariant(); }else return QVariant(); } switch (role) { case Qt::DecorationRole: return QIcon::fromTheme(item->iconName() + "-symbolic", QIcon::fromTheme(item->iconName())); case Qt::DisplayRole: return item->displayName(); case Qt::ToolTipRole: return item->displayName(); case Qt::UserRole: return item->uri(); default: break; } return QVariant(); } bool SideBarModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (data(index, role) != value) { // FIXME: Implement me! Q_EMIT dataChanged(index, index, QVector() << role); return true; } return false; } Qt::ItemFlags SideBarModel::flags(const QModelIndex &index) const { return QAbstractItemModel::flags(index) | Qt::ItemIsDropEnabled; // FIXME: Implement me! } bool SideBarModel::insertRows(int row, int count, const QModelIndex &parent) { beginInsertRows(parent, row, row + count - 1); // FIXME: Implement me! endInsertRows(); return true; } bool SideBarModel::insertColumns(int column, int count, const QModelIndex &parent) { beginInsertColumns(parent, column, column + count - 1); // FIXME: Implement me! endInsertColumns(); return true; } bool SideBarModel::removeRows(int row, int count, const QModelIndex &parent) { beginRemoveRows(parent, row, row + count - 1); // FIXME: Implement me! endRemoveRows(); return true; } bool SideBarModel::removeColumns(int column, int count, const QModelIndex &parent) { beginRemoveColumns(parent, column, column + count - 1); // FIXME: Implement me! endRemoveColumns(); return true; } void SideBarModel::onIndexUpdated(const QModelIndex &index) { auto item = itemFromIndex(index); //qDebug()<m_children->count(); bool isEmpty = true; for (auto child : *item->m_children) { auto info = FileInfo::fromUri(child->uri()); if (!info->displayName().startsWith(".") && (info->isDir() || info->isVolume())||item->uri()=="computer:///") isEmpty = false; if (child->type() == SideBarAbstractItem::SeparatorItem) { removeRows(item->m_children->indexOf(child), 1, index); item->m_children->removeOne(child); qDebug()<<"separator"<m_children->count(); } } if (isEmpty) { auto separator = new SideBarSeparatorItem(SideBarSeparatorItem::EmptyFile, item, this); item->m_children->append(separator); insertRows(item->m_children->count() - 1, 1, index); } } bool SideBarModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { auto urls = data->urls(); QStringList srcUris; if (data->hasFormat("peony-qt/encoded-uris")) { srcUris = data->text().split(" "); for (QString uri : srcUris) { if (uri.startsWith("recent://") || uri.startsWith("filesafe://")) srcUris.removeOne(uri); } } else { for (auto url : urls) { //can not drag file from recent if (url.url().startsWith("recent://") || url.url().startsWith("filesafe://")) return false; srcUris<itemFromIndex(parent); qDebug()<<"SideBarModel::dropMimeData:" <isLoaded()) { for (auto url : srcUris) { //FIXME: replace BLOCKING api in ui thread. auto info = FileInfo::fromUri(url); if (info->displayName().isNull()) { FileInfoJob j(info); j.querySync(); } if (info->isDir()) { bookmark->addBookMark(url); } } } return true; } switch (item->type()) { case SideBarAbstractItem::SeparatorItem: case SideBarAbstractItem::FavoriteItem: //drag to sider bar all as file move operation // { // auto bookmark = BookMarkManager::getInstance(); // if (bookmark->isLoaded()) { // for (auto url : data->urls()) { // auto info = FileInfo::fromUri(url.toDisplayString(), false); // if (info->displayName().isNull()) { // FileInfoJob j(info); // j.querySync(); // } // if (info->isDir()) { // bookmark->addBookMark(url.url()); // } // } // } // break; // } case SideBarAbstractItem::PersonalItem: case SideBarAbstractItem::FileSystemItem: { QStringList uris = srcUris; //can not drag file to recent if (item->uri().startsWith("recent://")) return false; for(auto uri : uris) { if (uri.startsWith("trash://")) return false; //can not drag file from recent if (uri.startsWith("recent://")) return false; //如果源文件路径是filesafe保护箱下的文件只支持复制,不支持移动; if(uri.startsWith("filesafe:///")) { action = Qt::CopyAction; } } if (action == Qt::MoveAction) { FileOperationUtils::move(uris, item->uri(), true, true); //qDebug() << "sideBarModel moveOp"; } else if (action == Qt::CopyAction) { FileOperationUtils::copy(uris, item->uri(), true); //qDebug() << "sideBarModel CopyOp"; } break; } } return true; } Qt::DropActions SideBarModel::supportedDropActions() const { return Qt::MoveAction|Qt::CopyAction; return Qt::MoveAction|Qt::CopyAction|Qt::LinkAction; } Qt::DropActions SideBarModel::supportedDragActions() const { return Qt::MoveAction; } peony/libpeony-qt/model/file-item-proxy-filter-sort-model.h0000644000175000017500000001110614205115226022741 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * Authors: Meihong He * */ #ifndef FILEITEMPROXYFILTERSORTMODEL_H #define FILEITEMPROXYFILTERSORTMODEL_H #include #include #include #include "peony-core_global.h" namespace Peony { class FileItem; class FileItemModel; class PEONYCORESHARED_EXPORT FileItemProxyFilterSortModel : public QSortFilterProxyModel { Q_OBJECT public: enum FilterFileType { ALL_TYPE, FILE_FOLDER, PICTURE, VIDEO, TXT_FILE, AUDIO, WPS_FILE, OTHERS }; Q_ENUM(FilterFileType) enum FilterFileModifyTime { ALL_TIME, TODAY, THIS_WEEK, THIS_MONTH, THIS_YEAR, YEAR_AGO }; Q_ENUM(FilterFileModifyTime) enum FilterFileSize { ALL_SIZE, EMPTY, TINY, SMALL, MEDIUM, BIG, LARGE, GREAT }; Q_ENUM(FilterFileSize) const QString Folder_Type = "inode/directory"; const QString Image_Type = "image/"; const QString Video_Type = "video/"; const QString Text_Type = "text/"; const QString Wps_Type = "application/wps-office"; const QString Audio_Type = "audio/"; explicit FileItemProxyFilterSortModel(QObject *parent = nullptr); void setSourceModel(QAbstractItemModel *model) override; void setShowHidden(bool showHidden); void setUseDefaultNameSortOrder(bool use); void setFolderFirst(bool folderFirst); void setFilterConditions(int fileType=0, int modifyTime=0, int fileSize=0); //multiple filter conditions for new advance search void addFileNameFilter(QString key, bool updateNow = false); void addFilterCondition(int option, int classify, bool updateNow = false); void removeFilterCondition(int option, int classify, bool updateNow = false); void clearConditions(); //set file label filter conditions, default value mean all files are accepted //use it without any paras can clear the filter conditions void setFilterLabelConditions(QString name = "", QColor color=Qt::transparent); //select multiple labels to filter files, file has any one of these label is accepted void setMutipleLabelConditions(QStringList names, QList colors); //give blur name to search color labels, can set CaseSensitive or not void setLabelBlurName(QString blurName = "", bool CaseSensitive = false); FileItem *itemFromIndex(const QModelIndex &proxyIndex); QModelIndex getSourceIndex(const QModelIndex &proxyIndex); const QModelIndex indexFromUri(const QString &uri); QStringList getAllFileUris(); QModelIndexList getAllFileIndexes(); public Q_SLOTS: void update(); protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; private: bool startWithChinese(const QString &displayName) const; bool checkFileTypeFilter(QString type) const; bool checkFileModifyTimeFilter(quint64 modifiedTime) const; bool checkFileSizeFilter(quint64 size) const; bool checkFileSizeOrTypeFilter(quint64 sizem, bool isDir) const; bool checkFileNameFilter(const QString &displayName) const; private: bool m_show_hidden; bool m_use_default_name_sort_order; bool m_folder_first; bool m_case_sensitive = false; QString m_blur_name = ""; QString m_label_name = ""; QColor m_label_color = Qt::transparent; const int ALL_FILE = 0; const quint64 K_BASE = 1000; int m_show_file_type=ALL_FILE, m_show_modify_time=ALL_FILE, m_show_file_size=ALL_FILE; QList m_file_type_list, m_modify_time_list, m_file_size_list; QStringList m_file_name_list; QStringList m_show_label_names; QList m_show_label_colors; }; } #endif // FILEITEMPROXYFILTERSORTMODEL_H peony/libpeony-qt/model/side-bar-separator-item.h0000644000175000017500000000572714205101223020765 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef SIDEBARSEPARATORITEM_H #define SIDEBARSEPARATORITEM_H #include #include "peony-core_global.h" #include "side-bar-abstract-item.h" namespace Peony { /*! * \brief The SideBarSeparatorItem class * \details * This class is used to help side bar layout. * It also provide 3 types of separator, large, small and empty-file. * The large separator is used to make a space for top of sidebar. * The small separator is used to make a space for different type root item (favorite, personal and computer). * The empty-file separator is used as a indicator of a side bar file system item directory without any child item. */ class PEONYCORESHARED_EXPORT SideBarSeparatorItem : public SideBarAbstractItem { Q_OBJECT public: enum Details { Large, EmptyFile, Small }; explicit SideBarSeparatorItem(Details type, SideBarAbstractItem *parentItem, SideBarModel *model, QObject *parent = nullptr); Details separatorType() { return m_type; } Type type() override { return SideBarAbstractItem::SeparatorItem; } QString uri() override { return nullptr; } QString displayName() override { return m_type==EmptyFile?tr("(No Sub Directory)"):nullptr; } QString iconName() override { return nullptr; } bool hasChildren() override { return false; } bool isRemoveable() override { return false; } bool isEjectable() override { return false; } bool isMountable() override { return false; } QModelIndex firstColumnIndex() override; QModelIndex lastColumnIndex() override; SideBarAbstractItem *parent() override { return m_parent; } public Q_SLOTS: void eject(GMountUnmountFlags ejectFlag) override {} void unmount() override {} void format() override {} void onUpdated() override {} void findChildren() override {} void findChildrenAsync() override {} void clearChildren() override {} private: SideBarAbstractItem *m_parent = nullptr; Details m_type; }; } #endif // SIDEBARSEPARATORITEM_H peony/libpeony-qt/model/file-item.cpp0000644000175000017500000007574314205115226016570 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-item.h" #include "file-enumerator.h" #include "file-info-job.h" #include "file-info-manager.h" #include "file-watcher.h" #include "file-utils.h" #include "file-operation-utils.h" #include "file-item-model.h" #include "thumbnail-manager.h" #include "gerror-wrapper.h" #include "bookmark-manager.h" #include "audio-play-manager.h" #include #include #include #include #include #include #include #include using namespace Peony; QString uri2FavoriteUri(const QString &sourceUri) { QUrl url = sourceUri; QString favoriteUri = "favorite://" + url.path() + "?schema=" + url.scheme(); return favoriteUri; } FileItem::FileItem(std::shared_ptr info, FileItem *parentItem, FileItemModel *model, QObject *parent) : QObject(parent) { m_parent = parentItem; m_info = info; m_children = new QVector(); m_model = model; m_backend_enumerator = new FileEnumerator(this); m_idle = new QTimer(this); m_idle->setInterval(0); m_idle->setSingleShot(true); connect(m_idle, &QTimer::timeout, this, [=]{ if (m_uris_to_be_removed.isEmpty()) return; QStringList favoriteUris; if (m_uris_to_be_removed.count() < 10) { // do normal remove for (auto uri : m_uris_to_be_removed) { for (int row = 0; row < m_children->count(); row++) { auto child = m_children->at(row); // 此处实际可靠性还有待验证 if (FileUtils::isSamePath(uri, child->uri())) { auto info = child->m_info; if (info->isDir()) { favoriteUris.append(uri2FavoriteUri(uri)); } m_model->beginRemoveRows(this->firstColumnIndex(), row, row); m_children->remove(row); delete child; m_model->endRemoveRows(); break; } } } BookMarkManager::getInstance()->removeBookMark(favoriteUris); return; } // do reset model int time0 = QTime::currentTime().msecsSinceStartOfDay(); qDebug()<<"execute deletion"; m_model->beginResetModel(); qDebug()<<"files deleted"<count(); row++) { auto child = m_children->at(row); // 此处实际可靠性还有待验证 if (FileUtils::isSamePath(uri, child->uri())) { auto info = child->m_info; if (info->isDir()) { favoriteUris.append(uri2FavoriteUri(uri)); } m_children->remove(row); delete child; break; } } } m_model->endResetModel(); BookMarkManager::getInstance()->removeBookMark(favoriteUris); int time1 = QTime::currentTime().msecsSinceStartOfDay(); qDebug()<<"excute deletion finished, cost"<releaseThumbnail(m_uris_to_be_removed); }); m_thumbnail_watcher = std::make_shared("thumbnail://"); connect(m_thumbnail_watcher.get(), &FileWatcher::fileChanged, this, [=](const QString &uri){ auto index = m_model->indexFromUri(uri); if (index.isValid()) { auto item = m_model->itemFromIndex(index); if (item) { /*! \note fix the probabilistic jamming while thumbnailing with list view. we have to only trigger first column index dataChanged signal, otherwise there will be probility stucked whole program. i'm not sure if it is a bug of qtreeview. */ //m_model->dataChanged(item->firstColumnIndex(), item->lastColumnIndex()); m_model->dataChanged(item->firstColumnIndex(), item->firstColumnIndex()); } } }); // avoid call any method when model is deleted. setParent(m_model); } FileItem::~FileItem() { //qDebug()<<"~FileItem"<uri(); Q_EMIT cancelFindChildren(); //disconnect(); for (auto child : *m_children) { delete child; } m_children->clear(); delete m_children; } bool FileItem::operator==(const FileItem &item) { //qDebug()<uri()<uri(); return this->m_info->uri() == item.m_info->uri(); } const QString FileItem::uri() { return m_info->uri(); } #include"file-operation-manager.h" QVector *FileItem::findChildrenSync() { Q_EMIT m_model->findChildrenStarted(); std::shared_ptr enumerator = std::make_shared(); enumerator->setEnumerateDirectory(m_info->uri()); enumerator->enumerateSync(); auto infos = enumerator->getChildren(); for (auto info : infos) { FileItem *child = new FileItem(info, this, m_model); m_children->append(child); FileInfoJob *job = new FileInfoJob(info); job->setAutoDelete(); job->querySync(); } //qDebug() << "FileItem findChildrenSync"; Q_EMIT m_model->findChildrenFinished(); return m_children; } void FileItem::findChildrenAsync() { auto info = FileInfo::fromUri(m_info.get()->uri()); auto infoJob = new FileInfoJob(info); infoJob->setAutoDelete(); infoJob->queryAsync(); if (m_expanded) return; Q_EMIT m_model->findChildrenStarted(); m_expanded = true; Peony::FileEnumerator *enumerator = new Peony::FileEnumerator; enumerator->setEnumerateDirectory(m_info->uri()); //NOTE: entry a new root might destroyed the current enumeration work. //the root item will be delete, so we should cancel the previous enumeration. enumerator->connect(this, &FileItem::cancelFindChildren, enumerator, &FileEnumerator::cancel); enumerator->connect(enumerator, &FileEnumerator::cancelled, m_model, [=](){ m_model->findChildrenFinished(); }); enumerator->connect(enumerator, &FileEnumerator::prepared, this, [=](std::shared_ptr err, const QString &targetUri, bool critical) { if (critical) { //Peony::AudioPlayManager::getInstance()->playWarningAudio(); QMessageBox::critical(nullptr, tr("Error"), err->message()); enumerator->cancel(); //fix bug#77594 enumerator->deleteLater(); return; } if (!targetUri.isNull()) { if (targetUri != this->uri()) { this->m_info = FileInfo::fromUri(targetUri); GFile *targetFile = g_file_new_for_uri(targetUri.toUtf8().constData()); QUrl targetUrl = targetUri; auto path = g_file_get_path(targetFile); enumerator->setEnumerateDirectory(targetFile); g_object_unref(targetFile); } enumerator->enumerateAsync(); return; } auto target = FileUtils::getTargetUri(m_info->uri()); if (!target.isEmpty()) { enumerator->cancel(); //enumerator->deleteLater(); m_model->setRootUri(target); return; } if (err) { qDebug()<<"file item error:" <message()<getEnumerateUri(); //Peony::AudioPlayManager::getInstance()->playWarningAudio(); if (err.get()->code() == G_IO_ERROR_NOT_FOUND || err.get()->code() == G_IO_ERROR_PERMISSION_DENIED) { enumerator->cancel(); //fix goto removed path in case device is ejected if (this->uri().startsWith("file:///media")) { //check bookmark and delete BookMarkManager::getInstance()->removeBookMark(uri2FavoriteUri(this->uri())); m_model->sendPathChangeRequest("computer:///"); } else { m_model->setRootUri(FileUtils::getParentUri(this->uri())); } 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"), tr("File not exist, do you want to delete the link file?")); if (result == QMessageBox::Yes) { qDebug() << "Delete unused symbollink."; QStringList selections; selections.push_back(this->uri()); FileOperationUtils::trash(selections, true); } } else if (err.get()->code() == G_IO_ERROR_PERMISSION_DENIED) { QMessageBox *msgBox = new QMessageBox(); msgBox->setWindowTitle(tr("Error")); QString errorInfo = tr("Can not open path \"%1\",permission denied.").arg(this->uri().unicode()); msgBox->setText(errorInfo); msgBox->setModal(false); msgBox->setAttribute(Qt::WA_DeleteOnClose); KWindowSystem::setState(msgBox->winId(), KWindowSystem::KeepAbove); msgBox->show(); //QMessageBox::critical(nullptr, tr("Error"), errorInfo); } 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); } return; } else { QMessageBox::critical(nullptr, tr("Error"), err->message()); enumerator->cancel(); return; } } enumerator->enumerateAsync(); }); if (!m_model->isPositiveResponse()) { enumerator->connect(enumerator, &Peony::FileEnumerator::enumerateFinished, this, [=](bool successed) { if (successed) { auto infos = enumerator->getChildren(); m_async_count = infos.count(); if (infos.count() == 0) { Q_EMIT m_model->findChildrenFinished(); } for (auto info : infos) { FileItem *child = new FileItem(info, this, m_model); m_children->prepend(child); FileInfoJob *job = new FileInfoJob(info); job->setAutoDelete(); /* FileInfo *shared_info = info.get(); int row = infos.indexOf(info); //qDebug()<uri()<connect(job, &FileInfoJob::infoUpdated, this, [=](){ qDebug()<iconName()<insertRows(0, m_children->count(), this->firstColumnIndex()); Q_EMIT this->m_model->findChildrenFinished(); Q_EMIT m_model->updated(); for (auto info : infos) { ThumbnailManager::getInstance()->createThumbnail(info->uri(), m_thumbnail_watcher); } } }); connect(job, &FileInfoJob::infoUpdated, child, [=](){ m_model->dataChanged(child->firstColumnIndex(), child->lastColumnIndex()); }); job->queryAsync(); } } else { //qDebug() << "enumerateFinished false" <findChildrenFinished(); return; } enumerator->cancel(); delete enumerator; m_watcher = std::make_shared(this->m_info->uri(), nullptr, true); m_watcher->setMonitorChildrenChange(true); connect(m_watcher.get(), &FileWatcher::fileCreated, this, [=](QString uri) { //add new item to m_children //tell the model update this->onChildAdded(uri); Q_EMIT this->childAdded(uri); qDebug() << "inpositive Model onChildAdded:" <onChildRemoved(uri); }); connect(m_watcher.get(), &FileWatcher::fileChanged, this, [=](const QString &uri) { auto index = m_model->indexFromUri(uri); if (index.isValid()) { auto infoJob = new FileInfoJob(FileInfo::fromUri(index.data(FileItemModel::UriRole).toString())); infoJob->setAutoDelete(); connect(infoJob, &FileInfoJob::queryAsyncFinished, this, [=]() { m_model->dataChanged(m_model->indexFromUri(uri), m_model->indexFromUri(uri)); //auto info = FileInfo::fromUri(uri); ThumbnailManager::getInstance()->createThumbnail(uri, m_thumbnail_watcher, true); /* if (info->isDesktopFile()) { ThumbnailManager::getInstance()->updateDesktopFileThumbnail(info->uri(), m_thumbnail_watcher); } */ }); infoJob->queryAsync(); } }); connect(m_watcher.get(), &FileWatcher::fileRenamed, this, [=](const QString &oldUri, const QString &newUri) { Q_EMIT this->renamed(oldUri, newUri); this->onRenamed(oldUri, newUri); }); connect(m_watcher.get(), &FileWatcher::thumbnailUpdated, this, [=](const QString &uri) { m_model->dataChanged(m_model->indexFromUri(uri), m_model->indexFromUri(uri)); }); connect(m_watcher.get(), &FileWatcher::directoryDeleted, this, [=](QString uri) { //clean all the children, if item index is root index, cd up. //this might use FileItemModel::setRootItem() Q_EMIT this->deleted(uri); this->onDeleted(uri); }); connect(m_watcher.get(), &FileWatcher::locationChanged, this, [=](QString oldUri, QString newUri) { //this might use FileItemModel::setRootItem() Q_EMIT this->renamed(oldUri, newUri); this->onRenamed(oldUri, newUri); }); connect(m_watcher.get(), &FileWatcher::directoryUnmounted, this, [=]() { m_model->sendPathChangeRequest("computer:///"); }); //qDebug()<<"startMonitor"; connect(m_watcher.get(), &FileWatcher::requestUpdateDirectory, this, &FileItem::onUpdateDirectoryRequest); m_watcher->startMonitor(); }); } else { enumerator->connect(enumerator, &Peony::FileEnumerator::childrenUpdated, this, [=](const QStringList &uris, bool isEnding) { if (uris.isEmpty()) { if (isEnding) { //qDebug() << "enumerateFinished childrenUpdated:" <findChildrenFinished(); Q_EMIT m_model->updated(); } } if (!m_children) { enumerator->disconnect(); delete enumerator; return ; } if (isEnding) { m_ending_uris.clear(); m_ending_uris = uris; } for (auto uri : uris) { auto info = FileInfo::fromUri(uri); auto infoJob = new FileInfoJob(info); infoJob->setAutoDelete(); infoJob->connect(infoJob, &FileInfoJob::infoUpdated, this, [=]() { auto item = new FileItem(info, this, m_model); m_model->beginInsertRows(firstColumnIndex(), m_children->count(), m_children->count()); m_children->append(item); m_model->endInsertRows(); //Q_EMIT m_model->dataChanged(item->firstColumnIndex(), item->lastColumnIndex()); //Q_EMIT m_model->updated(); ThumbnailManager::getInstance()->createThumbnail(info->uri(), m_thumbnail_watcher); m_ending_uris.removeOne(uri); if (isEnding && m_ending_uris.isEmpty()) { //qApp->processEvents(); Q_EMIT m_model->findChildrenFinished(); Q_EMIT m_model->updated(); } }); infoJob->queryAsync(); } }); enumerator->connect(enumerator, &Peony::FileEnumerator::enumerateFinished, this, [=](bool successed) { delete enumerator; if (!successed) { Q_EMIT m_model->findChildrenFinished(); return; } if (!m_model||!m_children||!m_info) return; m_watcher = std::make_shared(this->m_info->uri(), nullptr, true); m_watcher->setMonitorChildrenChange(true); connect(m_watcher.get(), &FileWatcher::fileCreated, this, [=](QString uri) { //add new item to m_children //tell the model update this->onChildAdded(uri); Q_EMIT this->childAdded(uri); qDebug() << "positive onChildAdded:" <createThumbnail(uri, m_thumbnail_watcher, true); }); connect(m_watcher.get(), &FileWatcher::fileDeleted, this, [=](QString uri) { this->onChildRemoved(uri); }); connect(m_watcher.get(), &FileWatcher::fileChanged, this, [=](const QString &uri) { auto index = m_model->indexFromUri(uri); if (index.isValid()) { auto infoJob = new FileInfoJob(FileInfo::fromUri(index.data(FileItemModel::UriRole).toString())); infoJob->setAutoDelete(); connect(infoJob, &FileInfoJob::queryAsyncFinished, this, [=]() { m_model->dataChanged(m_model->indexFromUri(uri), m_model->indexFromUri(uri)); //comment to fix endless loop and can not rename issue, related to bug#72642 // auto info = FileInfo::fromUri(uri); // if (info->isDesktopFile()) { // ThumbnailManager::getInstance()->updateDesktopFileThumbnail(info->uri(), m_watcher); // } }); infoJob->queryAsync(); } }); connect(m_watcher.get(), &FileWatcher::fileRenamed, this, [=](const QString &oldUri, const QString &newUri) { this->onRenamed(oldUri, newUri); BookMarkManager::getInstance()->bookmarkChanged(oldUri, newUri); }); connect(m_watcher.get(), &FileWatcher::thumbnailUpdated, this, [=](const QString &uri) { m_model->dataChanged(m_model->indexFromUri(uri), m_model->indexFromUri(uri)); }); connect(m_watcher.get(), &FileWatcher::directoryDeleted, this, [=](QString uri) { //clean all the children, if item index is root index, cd up. //this might use FileItemModel::setRootItem() Q_EMIT this->deleted(uri); this->onDeleted(uri); }); connect(m_watcher.get(), &FileWatcher::locationChanged, this, [=](QString oldUri, QString newUri) { //this might use FileItemModel::setRootItem() Q_EMIT this->renamed(oldUri, newUri); this->onRenamed(oldUri, newUri); }); connect(m_watcher.get(), &FileWatcher::directoryUnmounted, this, [=]() { m_model->sendPathChangeRequest("computer:///"); }); //qDebug()<<"startMonitor"; connect(m_watcher.get(), &FileWatcher::requestUpdateDirectory, this, &FileItem::onUpdateDirectoryRequest); m_watcher->startMonitor(); }); } enumerator->prepare(); } QModelIndex FileItem::firstColumnIndex() { return m_model->firstColumnIndex(this); } QModelIndex FileItem::lastColumnIndex() { return m_model->lastColumnIndex(this); } bool FileItem::hasChildren() { //qDebug()<<"has children"<uri()<<(m_info->isDir() || m_info->isVolume() || m_children->count() > 0); return m_info->isDir() || m_info->isVolume() || m_children->count() > 0; } FileItem *FileItem::getChildFromUri(QString uri) { // optimize auto index = m_model->indexFromUri(uri); if (index.isValid()) { return m_model->itemFromIndex(index); } return nullptr; /* for (auto item : *m_children) { QUrl itemUrl = item->uri(); QUrl url = uri; QString decodedUri = url.toDisplayString(); if (decodedUri == itemUrl.toDisplayString()) return item; } return nullptr; */ } void FileItem::onChildAdded(const QString &uri) { m_uris_to_be_removed.removeOne(uri); qDebug()<<"add child:" << uri; FileItem *child = getChildFromUri(uri); if (child) { qDebug()<<"has added, return"; //child info maybe changed, so need sync update again child->updateInfoAsync(); //m_model->updated(); return; } //add waiting queue to fix show item duplicated issue if (m_waiting_add_queue.contains(uri)) { qDebug()<<"is in m_waiting_add_queue, return"; return; } auto info = FileInfo::fromUri(uri); auto infoJob = new FileInfoJob(info); infoJob->setAutoDelete(); m_waiting_add_queue.append(uri); infoJob->connect(infoJob, &FileInfoJob::infoUpdated, this, [=]() { m_waiting_add_queue.removeOne(uri); auto item = getChildFromUri(uri); // add exsited checkment. link to: #66999 if (!item) { item = new FileItem(info, this, m_model); m_model->beginInsertRows(firstColumnIndex(), m_children->count(), m_children->count()); m_children->append(item); m_model->endInsertRows(); qDebug() <<"successfully added child:" <dataChanged(item->firstColumnIndex(), item->lastColumnIndex()); //Q_EMIT m_model->updated(); QTimer::singleShot(1000, this, [=](){ ThumbnailManager::getInstance()->createThumbnail(info->uri(), m_thumbnail_watcher); }); } else { qInfo()<<"file"<queryAsync(); // FileItem *newChild = new FileItem(FileInfo::fromUri(uri), this, m_model); // m_model->beginInsertRows(this->firstColumnIndex(), m_children->count(), m_children->count()); // m_children->append(newChild); // m_model->endInsertRows(); // //use sync update here. // newChild->updateInfoAsync(); // //m_model->updated(); } void FileItem::onChildRemoved(const QString &uri) { // fix #62925 m_waiting_add_queue.removeOne(uri); m_uris_to_be_removed.append(uri); if (!m_idle->isActive()) { m_idle->start(); } return; } void FileItem::onDeleted(const QString &thisUri) { qDebug()<<"deleted"; //FIXME: when a mount point unmounted, it was aslo assumed as "deleted", //in this case we should not delete this item here. //actually i don't think this desgin is good enough. maybe there is a //better choice. //another problem is that if we just clear the children of this item, //it will be "unexpandable". but the item index hasChildren() is true. //doublue clicked twice it will be expanded. a qt's bug? if (m_parent) { if (m_parent->m_info->uri() == thisUri) { m_model->removeRow(m_parent->m_children->indexOf(this), m_parent->firstColumnIndex()); m_parent->m_children->removeOne(this); } else { //if just clear children, there will be a small problem. clearChildren(); m_model->removeRow(m_parent->m_children->indexOf(this), m_parent->firstColumnIndex()); m_parent->m_children->removeOne(this); m_parent->onChildAdded(m_info->uri()); } this->deleteLater(); } else { //cd up. auto tmpItem = this; auto tmpUri = FileUtils::getParentUri(tmpItem->uri()); while(tmpItem && tmpUri.isNull()) { tmpUri = FileUtils::getParentUri(tmpUri); tmpItem = tmpItem->m_parent; } if (!tmpUri.isNull()) { if(tmpUri.startsWith("file:///media")) m_model->sendPathChangeRequest("computer:///"); else m_model->setRootUri(tmpUri); } else { m_model->setRootUri("file:///"); } } m_model->updated(); } void FileItem::onRenamed(const QString &oldUri, const QString &newUri) { qDebug()<<"renamed"; // fix #77076, which caused by idle removing item due to #66255. if (m_uris_to_be_removed.contains(newUri)) { m_uris_to_be_removed.removeOne(newUri); } // note that some times new file has arealy in directory view, // and there is no delete event triggered. for example. copy // a .desktop file in current view. in this case there might // be an outdated tmp file left. // // to avoid that we add an existing checkment, and handle old // file with different situation. auto newChild = getChildFromUri(newUri); if (newChild) { qDebug()<<"new child has arealy in view"; newChild->updateInfoAsync(); } FileItem *child = getChildFromUri(oldUri); if (child) { if (!newChild) { int index = m_children->indexOf(child); m_children->at(index)->m_info= FileInfo::fromUri(newUri); child->updateInfoAsync(); } else { m_model->beginRemoveRows(this->firstColumnIndex(), m_children->indexOf(child), m_children->indexOf(child)); m_children->removeOne(child); child->deleteLater(); m_model->endRemoveRows(); } } } void FileItem::onUpdateDirectoryRequest() { auto enumerator = new FileEnumerator(this); enumerator->setEnumerateDirectory(m_model->getRootUri()); connect(enumerator, &FileEnumerator::enumerateFinished, m_model, [=](){ if (m_model->getRootUri() != enumerator->getEnumerateUri()) return; auto currentUris = enumerator->getChildrenUris(); QStringList rawUris; QStringList removedUris; QStringList addedUris; QVector removeFileItem ; for (auto child : *m_model->m_root_item->m_children) { rawUris<uri(); if (!currentUris.contains(child->uri())) { removedUris<uri(); removeFileItem.append(child); //m_model->m_root_item->onChildRemoved(child->uri()); } } for (auto item : removeFileItem ) { m_model->m_root_item->onChildRemoved(item->uri()); } for (auto uri : currentUris) { if (!rawUris.contains(uri)) { addedUris<m_root_item->onChildAdded(uri); } } for (auto uri : currentUris) { if (!addedUris.contains(uri) && !removedUris.contains(uri)) { //m_model->m_root_item->getChildFromUri(uri)->updateInfoAsync(); } } enumerator->deleteLater(); }); enumerator->enumerateAsync(); } void FileItem::updateInfoSync() { FileInfoJob *job = new FileInfoJob(m_info); if (job->querySync()) { m_model->dataChanged(this->firstColumnIndex(), this->lastColumnIndex()); ThumbnailManager::getInstance()->createThumbnail(this->uri(), m_thumbnail_watcher, true); } job->deleteLater(); } void FileItem::updateInfoAsync() { FileInfoJob *job = new FileInfoJob(m_info); job->setAutoDelete(); job->connect(job, &FileInfoJob::infoUpdated, this, [=]() { m_model->dataChanged(this->firstColumnIndex(), this->lastColumnIndex()); ThumbnailManager::getInstance()->createThumbnail(this->uri(), m_thumbnail_watcher, true); }); job->queryAsync(); } void FileItem::removeChildren() { } void FileItem::clearChildren() { auto parent = firstColumnIndex(); m_model->removeRows(0, m_model->rowCount(parent), parent); for (auto child : *m_children) { delete child; } m_children->clear(); m_expanded = false; m_watcher.reset(); m_watcher = nullptr; } /* Func: if it isn't a vaild volume device,it should not be displayed. */ bool FileItem::shouldShow() { QString uri,unixDevice,displayName; uri = m_info->uri(); if(uri.isEmpty()) return false; if("computer:///root.link" == uri) return true; //non computer path, no need check //to fix sftp IO stuck issue if (! uri.startsWith("computer:///") || ! uri.endsWith(".drive")) return true; displayName = FileUtils::getFileDisplayName(uri); if(displayName.isEmpty()) return false; if (displayName.contains(":")) return true; //if needed, comment to not use this IO stuck API unixDevice = FileUtils::getUnixDevice(uri); if(!unixDevice.isEmpty()){ return false; } return true; } peony/libpeony-qt/model/file-item-model.cpp0000644000175000017500000004601214205115226017651 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #include "file-item-model.h" #include "file-item.h" #include "file-info.h" #include "file-info-job.h" #include "file-operation-manager.h" #include "file-move-operation.h" #include "file-copy-operation.h" #include "file-utils.h" #include "thumbnail-manager.h" #include "file-operation-utils.h" #include #include #include #include #include #include #include using namespace Peony; FileItemModel::FileItemModel(QObject *parent) : QAbstractItemModel (parent) { setPositiveResponse(true); } FileItemModel::~FileItemModel() { qDebug()<<"~FileItemModel"; disconnect(); if (m_root_item) delete m_root_item; } const QString FileItemModel::getRootUri() { if (!m_root_item) return nullptr; return m_root_item->uri(); } void FileItemModel::setRootUri(const QString &uri) { if (uri.isNull()) { setRootUri("file:///"); m_root_uri = "file:///"; return; } m_root_uri = uri; auto info = FileInfo::fromUri(uri); auto item = new FileItem(info, nullptr, this, this); setRootItem(item); } void FileItemModel::setRootItem(FileItem *item) { beginResetModel(); m_root_item->deleteLater(); m_root_item = item; m_root_item->findChildrenAsync(); endResetModel(); } QModelIndex FileItemModel::index(int row, int column, const QModelIndex &parent) const { //root children if (!parent.isValid()) { if (row < 0 || row > m_root_item->m_children->count()-1) return QModelIndex(); return createIndex(row, column, m_root_item->m_children->at(row)); } FileItem *item = static_cast(parent.internalPointer()); if (row < 0 || row > item->m_children->count()-1) return QModelIndex(); return createIndex(row, column, item->m_children->at(row)); } FileItem *FileItemModel::itemFromIndex(const QModelIndex &index) const { return static_cast(index.internalPointer()); } QModelIndex FileItemModel::firstColumnIndex(FileItem *item) { //root children if (item->m_parent == nullptr) { for (int i = 0; i < m_root_item->m_children->count(); i++) { //qDebug()<m_info->uri()<m_children->at(i)->m_info->uri(); if (item == m_root_item->m_children->at(i)) { //qDebug()<m_info->uri(); return createIndex(i, 0, item); } } return QModelIndex(); } else { //has parent item for (int i = 0; i < item->m_parent->m_children->count(); i++) { if (item == item->m_parent->m_children->at(i)) return createIndex(i, 0, item); } return QModelIndex(); } } QModelIndex FileItemModel::lastColumnIndex(FileItem *item) { if (!item->m_parent) { for (int i = 0; i < m_root_item->m_children->count(); i++) { //qDebug()<m_info->uri()<m_children->at(i)->m_info->uri(); if (item == m_root_item->m_children->at(i)) { //qDebug()<m_info->uri(); return createIndex(i, Other, item); } } return QModelIndex(); } else { //has parent item for (int i = 0; i < item->m_parent->m_children->count(); i++) { if (item == item->m_parent->m_children->at(i)) return createIndex(i, Other, item); } return QModelIndex(); } } const QModelIndex FileItemModel::indexFromUri(const QString &uri) { //FIXME: support recursively finding? for (auto child : *m_root_item->m_children) { GFile *left = g_file_new_for_uri(child->uri().toUtf8().constData()); GFile *right = g_file_new_for_uri(uri.toUtf8().constData()); bool equal = g_file_equal(left, right); g_object_unref(left); g_object_unref(right); if (equal) { return child->firstColumnIndex(); } } return QModelIndex(); } QModelIndex FileItemModel::parent(const QModelIndex &child) const { FileItem *childItem = static_cast(child.internalPointer()); //root children if (childItem->m_parent == nullptr) return QModelIndex(); return childItem->m_parent->firstColumnIndex(); } int FileItemModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return FileSize+1; } int FileItemModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) { if (!m_root_item) { return 0; } return m_root_item->m_children->count(); } FileItem *parent_item = static_cast(parent.internalPointer()); return parent_item->m_children->count(); } QVariant FileItemModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } FileItem *item = static_cast(index.internalPointer()); // we have to add uri role to every valid index, so that we can ensure // that we can open the file/directory correctly. if (role == FileItemModel::UriRole) return QVariant(item->uri()); //qDebug()<<"data:" <m_info->uri() << index.column(); switch (index.column()) { case FileName: { switch (role) { case Qt::TextAlignmentRole: { return QVariant(Qt::AlignHCenter | Qt::AlignBaseline); } case Qt::DisplayRole: { //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()); } case Qt::DecorationRole: { auto thumbnail = ThumbnailManager::getInstance()->tryGetThumbnail(item->m_info->uri()); if (!thumbnail.isNull()) { return thumbnail; } QIcon icon = QIcon::fromTheme(item->m_info->iconName(), QIcon::fromTheme("text-x-generic")); return QVariant(icon); } 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()); } default: return QVariant(); } } case ModifiedDate: { switch (role) { case Qt::DisplayRole: //can not sort, comment this //trash files show delete Date if (m_root_uri.startsWith("trash://") && !item->m_info->deletionDate().isNull()) { QDateTime deleteTime = QDateTime::fromMSecsSinceEpoch(item->m_info->deletionTime (), Qt::LocalTime); if (QGSettings::isSchemaInstalled("org.ukui.control-center.panel.plugins")) { QGSettings setting("org.ukui.control-center.panel.plugins"); QString val = setting.get ("date").toString (); if ("cn" == val) { return QVariant(deleteTime.toString ("yyyy/MM/dd HH:mm:ss")); } else { return QVariant(deleteTime.toString ("yyyy-MM-dd HH:mm:ss")); } } return QVariant(item->m_info->deletionDate()); } return QVariant(item->m_info->modifiedDate()); default: return QVariant(); } } case FileType: switch (role) { case Qt::DisplayRole: { if (item->m_info->isSymbolLink()) { return QVariant(tr("Symbol Link, ") + item->m_info->fileType()); } return QVariant(item->m_info->fileType()); } default: return QVariant(); } case FileSize: { switch (role) { case Qt::DisplayRole: { if (item->hasChildren()) { if (item->m_expanded) { return QVariant(QString::number(item->m_children->count()) + tr("child(ren)")); } return QVariant(); } return QVariant(item->m_info->fileSize()); } default: return QVariant(); } } default: return QVariant(); } } QVariant FileItemModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Vertical) return QVariant(); if (role == Qt::DisplayRole) { //qDebug() <<"headerData:" <(parent.internalPointer()); if (parent_item->hasChildren() && m_can_expand) return true; return false; } Qt::ItemFlags FileItemModel::flags(const QModelIndex &index) const { if (index.isValid()) { Qt::ItemFlags flags = QAbstractItemModel::flags(index); auto item = itemFromIndex(index); if (item->m_info->isDir()) { flags |= Qt::ItemIsDropEnabled; } if (index.column() == FileName) { flags |= Qt::ItemIsDragEnabled; flags |= Qt::ItemIsEditable; } return flags; } else { return Qt::ItemIsDropEnabled; } } bool FileItemModel::canFetchMore(const QModelIndex &parent) const { //qDebug()<<"canFetchMore"; if (!parent.isValid()) return true; FileItem *parent_item = static_cast(parent.internalPointer()); if (!parent_item->m_expanded) { return true; } return false; } void FileItemModel::fetchMore(const QModelIndex &parent) { Q_UNUSED(parent); //do not fetch more here } bool FileItemModel::insertRows(int row, int count, const QModelIndex &parent) { beginInsertRows(parent, row, row + count - 1); // FIXME: Implement me! endInsertRows(); return true; } bool FileItemModel::insertColumns(int column, int count, const QModelIndex &parent) { beginInsertColumns(parent, column, column + count - 1); // FIXME: Implement me! endInsertColumns(); return true; } bool FileItemModel::removeRows(int row, int count, const QModelIndex &parent) { beginRemoveRows(parent, row, row + count - 1); // FIXME: Implement me! endRemoveRows(); return true; } bool FileItemModel::removeColumns(int column, int count, const QModelIndex &parent) { beginRemoveColumns(parent, column, column + count - 1); // FIXME: Implement me! endRemoveColumns(); return true; } void FileItemModel::onFoundChildren(const QModelIndex &parent) { if (!parent.isValid()) { return; } FileItem *parentItem = static_cast(parent.internalPointer()); beginInsertRows(parent, 0, parentItem->m_children->count() - 1); endInsertRows(); } void FileItemModel::onItemAdded(FileItem *item) { if (!item->m_parent) insertRow(item->firstColumnIndex().row()); insertRow(item->firstColumnIndex().row(), item->m_parent->firstColumnIndex()); } void FileItemModel::onItemRemoved(FileItem *item) { if (!item->m_parent) removeRow(item->firstColumnIndex().row()); removeRow(item->firstColumnIndex().row(), item->m_parent->firstColumnIndex()); } void FileItemModel::cancelFindChildren() { qDebug()<<"cancel"; m_root_item->cancelFindChildren(); } void FileItemModel::setRootIndex(const QModelIndex &index) { //NOTE: if we use proxy model, we might get the wrong item from index. //add the new data role save the file's uri to resolve this problem. if (index.isValid()) { auto new_root_info = FileInfo::fromUri(index.data(UriRole).toString()); auto new_root_item = new FileItem(new_root_info, nullptr, this); if (new_root_item->hasChildren()) { setRootItem(new_root_item); } } } QMimeData *FileItemModel::mimeData(const QModelIndexList &indexes) const { QMimeData* data = QAbstractItemModel::mimeData(indexes); //set urls data URLs correspond to the MIME type text/uri-list. QList urls; QStringList uris; for (auto index : indexes) { auto item = itemFromIndex(index); auto encodeUrl = Peony::FileUtils::urlEncode(item->m_info->uri()); QUrl url = encodeUrl; if (!urls.contains(url)) { qDebug() << "mimeData:" << url; urls << url; uris << encodeUrl; } } data->setUrls(urls); auto string = uris.join(" "); data->setData("peony-qt/encoded-uris", string.toUtf8()); data->setText(string); return data; } Qt::DropActions FileItemModel::supportedDropActions() const { //qDebug()<<"supportedDropActions"; return Qt::MoveAction|Qt::CopyAction; } Qt::DropActions FileItemModel::supportedDragActions() const { return Qt::MoveAction; } bool FileItemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { qDebug()<<"drop mime data"; //judge the drop dest uri. QString destDirUri = nullptr; if (parent.isValid()) { QModelIndex child = index(row, column, parent); if (child.isValid()) { //unexpected drop. /* auto item = static_cast(child.internalPointer()); qDebug()<m_info->uri(); if (item->m_info->isDir()) { destDirUri = item->m_info->uri(); } */ } else { //drop on a folder item. auto parentItem = itemFromIndex(parent); destDirUri = parentItem->m_info->uri(); } } else { //FIXME: for a mounted volume (for example, computer:///), //we have to set the dest dir uri as its mount point. //maybe i should do this when set model root item. destDirUri = m_root_item->m_info->uri(); auto targetUri = FileUtils::getTargetUri(destDirUri); if (!targetUri.isEmpty()) { destDirUri = targetUri; } } //if destDirUri was not set, do not execute a drop. if (destDirUri.isNull()) { return false; } auto info = Peony::FileInfo::fromUri(destDirUri); //qDebug() << "FileItemModel::dropMimeData:" <isDir() <type(); //if (!FileUtils::getFileIsFolder(destDirUri)) //fix drag file to folder symbolic fail issue if (! info->isDir() && ! destDirUri.startsWith("trash:///")) return false; //NOTE: //do not allow drop on it self. auto urls = data->urls(); QStringList srcUris; if (data->hasFormat("peony-qt/encoded-uris")) { srcUris = data->text().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<getOperationInfo(); auto targetUris = opInfo.get()->dests(); Q_EMIT this->selectRequest(targetUris); // auto selectionModel = new QItemSelectionModel(this); // selectionModel->clearSelection(); // QTimer::singleShot(1000, selectionModel, [=](){ // for (auto destUri : targetUris) { // auto index = indexFromUri(destUri); // selectionModel->select(index, QItemSelectionModel::Select); // } // selectionModel->deleteLater(); // }); }, Qt::BlockingQueuedConnection); //NOTE: //we have to handle the dnd with file operation, so do not //use QAbstractModel::dropMimeData() here; return true; } void FileItemModel::sendPathChangeRequest(const QString &uri) { Q_EMIT this->changePathRequest(uri, true, true); } peony/libpeony-qt/model/side-bar-net-work-item.h0000644000175000017500000000635014205101223020524 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2021, 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 . * * Authors: Wenfei He * */ #ifndef SIDEBARNETWORKITEM_H #define SIDEBARNETWORKITEM_H #include "peony-core_global.h" #include "side-bar-abstract-item.h" #include "usershare-manager.h" #include #include #include namespace Peony { class SideBarModel; class SharedDirectorInfoThread; class FileWatcher; class PEONYCORESHARED_EXPORT SideBarNetWorkItem : public SideBarAbstractItem { Q_OBJECT public: explicit SideBarNetWorkItem(const QString &uri, const QString &iconName, const QString &displayName, SideBarAbstractItem *parentItem, SideBarModel *model, QObject *parent = nullptr); Type type() override { return SideBarAbstractItem::NetWorkItem; } QString uri() override; QString displayName() override; QString iconName() override; bool hasChildren() override; bool isRemoveable() override{ return true; } bool isEjectable() override{ return false; } bool isMountable() override{ return false; } bool isUnmountable() override; bool isMounted() override; QModelIndex firstColumnIndex() override; QModelIndex lastColumnIndex() override; SideBarAbstractItem *parent() override { return m_parentItem; } void onUpdated() override {}; void eject(GMountUnmountFlags ejectFlag) override {}; void unmount() override; void realUnmount(); void format() override {}; void ejectOrUnmount() override; void findChildren() override; void findChildrenAsync() override; void clearChildren() override; void findRemoteServers(); void querySharedFolders(); public Q_SLOTS: void slot_addSharedFolder(const ShareInfo& shareInfo, bool successed); void slot_deleteSharedFolder(const QString& originalPath, bool successed); void slot_updateRemoteServer(const QString& server, bool add); protected: void initWatcher(); void startWatcher(); void stopWatcher(); private: SideBarAbstractItem *m_parentItem = nullptr; std::shared_ptr m_watcher = nullptr; }; class SharedDirectoryInfoThread : public QThread { Q_OBJECT public: explicit SharedDirectoryInfoThread(); protected: void run() override; Q_SIGNALS: void querySharedInfoFinish(QHash sharedFolderInfoMap); }; } #endif //SIDEBARNETWORKITEM_H peony/libpeony-qt/model/side-bar-vfs-item.cpp0000644000175000017500000000366114205115226020121 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "side-bar-vfs-item.h" #include "vfs-plugin-iface.h" #include "vfs-plugin-manager.h" #include "file-utils.h" #include "side-bar-model.h" using namespace Peony; SideBarVFSItem::SideBarVFSItem(VFSPluginIface *plugin, SideBarModel *model, QObject *parent) : SideBarAbstractItem(model, parent), m_plugin(plugin) { } QString SideBarVFSItem::uri() { if (m_uri.isEmpty()) { m_uri = m_plugin->uriScheme() + "/"; } return m_uri; } QString SideBarVFSItem::displayName() { if (m_display_name.isEmpty()) { auto vfsFile = VFSPluginManager::getInstance()->newVFSFile(uri()); auto info = g_file_query_info(vfsFile, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); m_display_name = g_file_info_get_display_name(info); g_object_unref(info); g_object_unref(vfsFile); } return m_display_name; } QString SideBarVFSItem::iconName() { return m_plugin->icon().name(); } QModelIndex SideBarVFSItem::firstColumnIndex() { return m_model->firstColumnIndex(this); } QModelIndex SideBarVFSItem::lastColumnIndex() { return m_model->lastColumnIndex(this); } peony/libpeony-qt/model/side-bar-abstract-item.cpp0000644000175000017500000000331214205115226021117 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "side-bar-abstract-item.h" #include "side-bar-model.h" #include using namespace Peony; SideBarAbstractItem::SideBarAbstractItem(SideBarModel *model, QObject *parent) : QObject(parent),m_model(model) { m_children = new QVector(); connect(this, &SideBarAbstractItem::queryInfoFinished, m_model, [=](){ m_model->dataChanged(firstColumnIndex(), lastColumnIndex()); }); } SideBarAbstractItem::~SideBarAbstractItem() { //qDebug()<<"~SideBarAbstractItem"; for (auto child : *m_children) { delete child; } delete m_children; } void SideBarAbstractItem::clearChildren() { m_model->removeRows(0, m_children->count(), firstColumnIndex()); for (auto child : *m_children) { m_children->removeOne(child); child->deleteLater(); } m_children->clear(); //qDebug()<<"clear children has children"<hasChildren(firstColumnIndex()); } peony/libpeony-qt/model/side-bar-favorite-item.h0000644000175000017500000000436614205101223020602 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef SIDEBARFAVORITEITEM_H #define SIDEBARFAVORITEITEM_H #include "peony-core_global.h" #include "side-bar-abstract-item.h" namespace Peony { class PEONYCORESHARED_EXPORT SideBarFavoriteItem : public SideBarAbstractItem { Q_OBJECT public: explicit SideBarFavoriteItem(QString uri, SideBarFavoriteItem *parentItem, SideBarModel *model, QObject *parent = nullptr); Type type() override; QString uri() override; QString displayName() override; QString iconName() override; bool hasChildren() override; bool isRemoveable() override { return false; } bool isEjectable() override { return false; } bool isMountable() override { return false; } QModelIndex firstColumnIndex() override; QModelIndex lastColumnIndex() override; SideBarAbstractItem *parent() override { return m_parent; } private: void initChildren(); public Q_SLOTS: void eject(GMountUnmountFlags ejectFlag) override {} void unmount() override {} void format() override {} void onUpdated() override {} void findChildren() override {} void findChildrenAsync() override {} void clearChildren() override {} private: void syncBookMark(); QString getTargetUri(const QString& uri); SideBarFavoriteItem *m_parent = nullptr; bool m_is_root_child = false; }; } #endif // SIDEBARFAVORITEITEM_H peony/libpeony-qt/model/side-bar-single-item.h0000644000175000017500000000456714205101223020247 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2021, 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 . * * Authors: Yue Lan * */ #ifndef SIDEBARSINGLEITEM_H #define SIDEBARSINGLEITEM_H #include "side-bar-abstract-item.h" namespace Peony { class FileInfo; class SideBarSingleItem : public SideBarAbstractItem { Q_OBJECT public: explicit SideBarSingleItem(const QString &uri, const QString &iconName, const QString &displayName, SideBarModel *model, QObject *parent = nullptr); explicit SideBarSingleItem(const QString &uri, SideBarModel *model, QObject *parent = nullptr); Type type() override { return SideBarAbstractItem::SingleItem; } QString uri() override; QString displayName() override; QString iconName() override; bool hasChildren() override { return false; } bool isRemoveable() override { return false; } bool isEjectable() override { return false; } bool isMountable() override { return false; } //TODO: monitoring the mount state bool isMounted() override { return false; } QModelIndex firstColumnIndex() override; QModelIndex lastColumnIndex() override; SideBarAbstractItem *parent() override { return nullptr; } public Q_SLOTS: void eject(GMountUnmountFlags ejectFlag) override {} void unmount() override {} void format() override {} void ejectOrUnmount() override {} void onUpdated() override {} void findChildren() override {} void findChildrenAsync() override {} void clearChildren() override {} private: QString m_uri; QString m_icon_name; QString m_display_name; std::shared_ptr m_info; }; } #endif // SIDEBARSINGLEITEM_H peony/libpeony-qt/model/path-bar-model.cpp0000644000175000017500000000612714205101223017467 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "path-bar-model.h" #include "file-enumerator.h" #include "file-info.h" #include "file-utils.h" #include "file-info-job.h" #include using namespace Peony; PathBarModel::PathBarModel(QObject *parent) : QStringListModel (parent) { m_enumerator = new FileEnumerator(this); m_enumerator->setEnumerateWithInfoJob(); connect(m_enumerator, &FileEnumerator::enumerateFinished, this, [=](bool successed){ if (successed) { m_childrens = m_enumerator->getChildren(); QStringList list; for (auto info : m_childrens) { if (!(info->isDir() || info->isVolume())) continue; if (info.get()->displayName().startsWith(".")) continue; QUrl url = info.get()->uri(); list<uri(), info.get()->displayName()); } beginResetModel(); setStringList(list); sort(0); endResetModel(); Q_EMIT updated(); } else { beginResetModel(); setStringList(QStringList()); endResetModel(); } }); } void PathBarModel::setRootPath(const QString &path, bool force) { setRootUri("file://" + path, force); } void PathBarModel::setRootUri(const QString &uri, bool force) { if (!force) { if (uri.contains("////")) return; if (m_current_uri == uri) return; } m_current_uri = uri; m_enumerator->cancel(); m_uri_display_name_hash.clear(); m_info = FileInfo::fromUri(uri); auto infoJob = new FileInfoJob(m_info); infoJob->setAutoDelete(); connect(infoJob, &FileInfoJob::queryAsyncFinished, this, [=](bool successed){ if (successed) { m_enumerator->cancel(); m_enumerator->setEnumerateDirectory(m_info.get()->uri()); m_enumerator->enumerateAsync(); } }); infoJob->queryAsync(); return; } QString PathBarModel::findDisplayName(const QString &uri) { QUrl url = uri; if (m_uri_display_name_hash.find(url.toDisplayString())->isNull()) { return FileUtils::getFileDisplayName(uri); } else { return m_uri_display_name_hash.value(url.toDisplayString()); } } peony/libpeony-qt/model/side-bar-net-work-item.cpp0000644000175000017500000002753214205115226021074 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2021, 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 . * * Authors: Wenfei He * */ #include "side-bar-net-work-item.h" #include "side-bar-model.h" #include "global-settings.h" #include "file-info.h" #include "file-info-job.h" #include "connect-to-server-dialog.h" #include "sync-thread.h" //#include "volume-manager.h" #include "volumeManager.h" #include "gobject-template.h" #include "file-utils.h" #include "libnotify/notification.h" #include #include #include "file-watcher.h" #include #include #include using namespace Peony; void notifyUser(QString notifyContent); SideBarNetWorkItem::SideBarNetWorkItem(const QString &uri, const QString &iconName, const QString &displayName, SideBarAbstractItem *parentItem, SideBarModel *model, QObject *parent) : SideBarAbstractItem(model, parent), m_parentItem(parentItem) { m_uri = uri; m_iconName = iconName; m_displayName = displayName; auto userShareManager = UserShareInfoManager::getInstance(); connect(userShareManager, &UserShareInfoManager::signal_addSharedFolder, this, &SideBarNetWorkItem::slot_addSharedFolder); connect(userShareManager, &UserShareInfoManager::signal_deleteSharedFolder, this, &SideBarNetWorkItem::slot_deleteSharedFolder); connect(GlobalSettings::getInstance(), &GlobalSettings::signal_updateRemoteServer,this,&SideBarNetWorkItem::slot_updateRemoteServer); qRegisterMetaType >("QHash"); } QString SideBarNetWorkItem::uri() { return m_uri; } QString SideBarNetWorkItem::displayName() { return m_displayName; } QString SideBarNetWorkItem::iconName() { return m_iconName; } bool SideBarNetWorkItem::hasChildren() { return (m_parentItem == nullptr); } bool SideBarNetWorkItem::isUnmountable() { /* 远程服务器返回true */ if (!m_uri.startsWith("file://")&& m_uri!="network:///"){ return true; } return false; } bool SideBarNetWorkItem::isMounted() { if(FileUtils::isMountPoint(m_uri)) return true; return false; } QModelIndex SideBarNetWorkItem::firstColumnIndex() { return m_model->firstColumnIndex(this); } QModelIndex SideBarNetWorkItem::lastColumnIndex() { return m_model->lastColumnIndex(this); } static void unmount_finished(GMount *mount, GAsyncResult *result) { GError *err = nullptr; g_mount_unmount_with_operation_finish(mount, result, &err); if (err) { if(!strcmp(err->message,"Not authorized to perform operation")){//umount /data need permissions. g_error_free(err); return; } if(strstr(err->message,"umount: ")){ QMessageBox::warning(nullptr,QObject::tr("Unmount failed"),QObject::tr("Unable to unmount it, you may need to close some programs, such as: GParted etc."),QMessageBox::Yes); g_error_free(err); return; } QMessageBox::warning(nullptr, QObject::tr("Unmount failed"), QObject::tr("Error: %1\n").arg(err->message), QMessageBox::Yes); g_error_free(err); } else { /* 卸载完成信息提示 */ QString unmountNotify = QObject::tr("Data synchronization is complete,the device has been unmount successfully!"); SyncThread::notifyUser(unmountNotify); } } void SideBarNetWorkItem::realUnmount() { //auto mount = Experimental_Peony::VolumeManager::getMountFromUri(this->uri().toUtf8().constData()); GFile *gFile= g_file_new_for_uri(this->uri().toUtf8().constData()); GMount *gMount = g_file_find_enclosing_mount(gFile, nullptr, nullptr); g_mount_unmount_with_operation(gMount, G_MOUNT_UNMOUNT_NONE, nullptr, nullptr, GAsyncReadyCallback(unmount_finished), nullptr); } void SideBarNetWorkItem::ejectOrUnmount() { unmount(); } void SideBarNetWorkItem::unmount() { SyncThread *syncThread = new SyncThread(m_uri); QThread* currentThread = new QThread(); syncThread->moveToThread(currentThread); connect(currentThread,&QThread::started,syncThread,&SyncThread::parentStartedSlot); connect(syncThread,&SyncThread::syncFinished,this,[=](){ realUnmount(); syncThread->disconnect(this); syncThread->deleteLater(); currentThread->disconnect(SIGNAL(started())); }); currentThread->start(); } void SideBarNetWorkItem::clearChildren() { stopWatcher(); SideBarAbstractItem::clearChildren(); } void SideBarNetWorkItem::findChildrenAsync() { findChildren(); } #include "file-utils.h" #include void SideBarNetWorkItem::findChildren() { //只有根节点才设置子节点 if (m_parentItem == nullptr) { clearChildren(); findRemoteServers(); querySharedFolders(); } /* 计算机视图卸载时,侧边栏item状态也要响应 */ this->initWatcher(); this->m_watcher->setMonitorChildrenChange(); connect(m_watcher.get(), &FileWatcher::fileDeleted, this, [=](const QString &uri) { for (auto item : *m_children) { if(QUrl(item->uri()).host()==QUrl(uri).host()) m_model->dataChanged(item->firstColumnIndex(), item->lastColumnIndex()); } }); this->startWatcher(); } void SideBarNetWorkItem::findRemoteServers() { if (m_parentItem == nullptr) { //获取连接过的服务器 QMap remoteServer = GlobalSettings::getInstance()->getValue(REMOTE_SERVER_REMOTE_IP).toMap (); for (const QString& remoteServer : remoteServer.keys ()) { if (!remoteServer.isEmpty()) { SideBarNetWorkItem *item = new SideBarNetWorkItem(remoteServer, "network-workgroup-symbolic", remoteServer, this, m_model, this); m_model->beginInsertRows(this->firstColumnIndex(), m_children->count(), m_children->count()); m_children->append(item); m_model->endInsertRows(); } } } } void SideBarNetWorkItem::querySharedFolders() { if (m_parentItem != nullptr) return; //获取共享文件夹很慢,所以使用单独的线程处理 - Obtaining shared folders is slow, so use a separate thread for processing SharedDirectoryInfoThread *thread = new SharedDirectoryInfoThread(); connect(thread, &SharedDirectoryInfoThread::querySharedInfoFinish, this, [=](QHash sharedFolderInfoMap) { delete thread; QHash::iterator iter; for(iter = sharedFolderInfoMap.begin();iter != sharedFolderInfoMap.end();iter++){ QString sharePath=iter.value(); QString shareName=iter.key(); if (!sharePath.isEmpty()) { SideBarNetWorkItem *item = new SideBarNetWorkItem("file://" + sharePath,"inode-directory", shareName,this,m_model); m_model->beginInsertRows(this->firstColumnIndex(), m_children->count(), m_children->count()); m_children->append(item); m_model->endInsertRows(); } } }); thread->start(); } void SideBarNetWorkItem::slot_addSharedFolder(const ShareInfo &shareInfo, bool successed) { if (!successed) return; if (!shareInfo.originalPath.isEmpty()) { SideBarNetWorkItem *item = new SideBarNetWorkItem("file://" + shareInfo.originalPath, "inode-directory", shareInfo.name, this, m_model, this); m_children->append(item); m_model->insertRows(m_children->count() - 1, 1, this->firstColumnIndex()); } return; } void SideBarNetWorkItem::slot_deleteSharedFolder(const QString& originalPath, bool successed) { if(!successed) return; for (auto item : *m_children){ if(item->uri()!="file://" + originalPath) continue; m_model->removeRow(m_children->indexOf(item), this->firstColumnIndex()); m_children->removeOne(item); } return; } void SideBarNetWorkItem::slot_updateRemoteServer(const QString& server,bool add) { if(add){ SideBarNetWorkItem *item = new SideBarNetWorkItem(server, "network-workgroup-symbolic", server, this, m_model, this); m_children->append(item); m_model->insertRows(m_children->count() - 1, 1, this->firstColumnIndex()); } else{ for (auto item : *m_children){ if(item->uri()!= server) continue; m_model->removeRow(m_children->indexOf(item), this->firstColumnIndex()); m_children->removeOne(item); } } } void SideBarNetWorkItem::initWatcher() { /* 监听计算机视图的卸载信号 */ if (!m_watcher) { m_watcher = std::make_shared("computer:///"); } } void SideBarNetWorkItem::startWatcher() {initWatcher(); m_watcher->startMonitor(); } void SideBarNetWorkItem::stopWatcher() { initWatcher(); m_watcher->stopMonitor(); } SharedDirectoryInfoThread::SharedDirectoryInfoThread() { } void SharedDirectoryInfoThread::run() { /** * \brief 获取全部共享文件夹的共享名称 * 在编写代码时的输出格式为: * 888 * * 如果输出发生改变,视情况修改 */ QStringList args; args.append("usershare"); args.append("list"); args.append(""); bool ret = false; auto userShareManager = UserShareInfoManager::getInstance(); QString shareNames=userShareManager->exectueCommand(args,&ret); QHash sharedFolderInfoMap;/* key:shareName,value: sharePath */ for (QString shareName : shareNames.split(QRegExp("\\s+"))) { if (!shareName.isEmpty()) { /** * \brief 根据共享名称获取详细信息 * 在编写代码时的输出格式为: * [888] * path=/home/hxf/888 * comment=Peony-Qt-Share-Extension * usershare_acl=Everyone:R, * guest_ok=n * * 如果输出发生改变,视情况修改 */ const Peony::ShareInfo* shareInfo = userShareManager->getShareInfo(shareName); QString sharePath = shareInfo->originalPath; if (!sharePath.isEmpty()) sharedFolderInfoMap.insert(shareName,sharePath); } } Q_EMIT querySharedInfoFinish(sharedFolderInfoMap); } peony/libpeony-qt/model/side-bar-vfs-item.h0000644000175000017500000000446514205115226017571 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef SIDEBARVFSITEM_H #define SIDEBARVFSITEM_H #include "peony-core_global.h" #include "side-bar-abstract-item.h" namespace Peony { class SideBarModel; class VFSPluginIface; class PEONYCORESHARED_EXPORT SideBarVFSItem : public SideBarAbstractItem { Q_OBJECT public: explicit SideBarVFSItem(VFSPluginIface *plugin, SideBarModel *model, QObject *parent = nullptr); Type type() override { return SideBarAbstractItem::FileSystemItem; } QString uri() override; QString displayName() override; QString iconName() override; bool hasChildren() override { return false; } bool isRemoveable() override { return false; } bool isEjectable() override { return false; } bool isMountable() override { return false; } //TODO: monitoring the mount state bool isMounted() override { return false; } QModelIndex firstColumnIndex() override; QModelIndex lastColumnIndex() override; SideBarAbstractItem *parent() override { return nullptr; } public Q_SLOTS: void eject(GMountUnmountFlags ejectFlag) override {} void unmount() override {} void format() override {} void ejectOrUnmount() override {} void onUpdated() override {} void findChildren() override {} void findChildrenAsync() override {} void clearChildren() override {} private: VFSPluginIface *m_plugin = nullptr; QString m_uri; QString m_display_name; SideBarModel *m_model = nullptr; }; } #endif // SIDEBARVFSITEM_H peony/libpeony-qt/model/side-bar-proxy-filter-sort-model.h0000644000175000017500000000302614205115226022556 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef SIDEBARPROXYFILTERSORTMODEL_H #define SIDEBARPROXYFILTERSORTMODEL_H #include "peony-core_global.h" #include #include #include namespace Peony { class SideBarAbstractItem; class PEONYCORESHARED_EXPORT SideBarProxyFilterSortModel : public QSortFilterProxyModel { Q_OBJECT public: explicit SideBarProxyFilterSortModel(QObject *parent = nullptr); SideBarAbstractItem *itemFromIndex(const QModelIndex &proxy_index); protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; private: QLocale m_locale; QCollator m_comparer; }; } #endif // SIDEBARPROXYFILTERSORTMODEL_H peony/libpeony-qt/model/path-bar-model.h0000644000175000017500000000414614205101223017133 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef PATHBARMODEL_H #define PATHBARMODEL_H #include "peony-core_global.h" #include #include #include namespace Peony { class FileInfo; class FileEnumerator; /*! * \brief The PathBarModel class * \details * PathBarModel is desgin for path completions. * This model would cache a directory driect children when the target uri * was set. * \note * A completion is theoretically responsive, so the enumeration of model * items should be as fast as possible. * It must be fast and lightweight enough to keep the ui-frequency. * For now, it performs well at local file system, but not good enough at * remote fs. */ class PEONYCORESHARED_EXPORT PathBarModel : public QStringListModel { Q_OBJECT public: explicit PathBarModel(QObject *parent = nullptr); QString findDisplayName(const QString &uri); QString currentDirUri() { return m_current_uri; } Q_SIGNALS: void updated(); public Q_SLOTS: void setRootPath(const QString &path, bool force = false); void setRootUri(const QString &uri, bool force = false); private: QString m_current_uri = nullptr; QHash m_uri_display_name_hash; std::shared_ptr m_info; QList> m_childrens; FileEnumerator *m_enumerator = nullptr; }; } #endif // PATHBARMODEL_H peony/libpeony-qt/model/file-item.h0000644000175000017500000001215414205115226016220 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILEITEM_H #define FILEITEM_H #include "peony-core_global.h" #include #include #include class QTimer; namespace Peony { class FileInfo; class FileInfoManager; class FileItemModel; class FileWatcher; class FileItemProxyFilterSortModel; class FileEnumerator; /*! * \brief The FileItem class *
* FileItem is the absctract item class contract with FileItemModel. * The different from FileInfo to FileItem is that FileItem has concept of * children and parent. This makes FileItem instance has a tree struction and * can represent a tree item in a view(non-tree as well). Other different is * that FileItem instance is not shared. You can hold many FileItem instances * crosponding to the same FileInfo, but they are allocated in their own memory * space. Every FileItem instance which has children will aslo support * monitoring. When find the children of the item, it will start a monitor for * this directory. *
* \note * Actually, every FileItem instance should bind with an model instance, * otherwise it will be useless. */ class PEONYCORESHARED_EXPORT FileItem : public QObject { friend class FileItemProxyFilterSortModel; friend class FileItemModel; Q_OBJECT public: explicit FileItem(std::shared_ptr info, FileItem *parentItem = nullptr, FileItemModel *model = nullptr, QObject *parent = nullptr); ~FileItem(); const QString uri(); const std::shared_ptr info() { return m_info; } bool operator == (const FileItem &item); QVector *findChildrenSync(); void findChildrenAsync(); /*! * \brief firstColumnIndex * \return first column index of item in model. * \see FileItemModel::firstColumnIndex(). */ QModelIndex firstColumnIndex(); /*! * \brief lastColumnIndex * \return last column index of item in model. * \see FileItemModel::lastColumnIndex() */ QModelIndex lastColumnIndex(); bool hasChildren(); bool shouldShow(); Q_SIGNALS: void cancelFindChildren(); void childAdded(const QString &uri); void childRemoved(const QString &uri); void deleted(const QString &thisUri); void renamed(const QString &oldUri, const QString &newUri); public Q_SLOTS: void onChildAdded(const QString &uri); void onChildRemoved(const QString &uri); void onDeleted(const QString &thisUri); void onRenamed(const QString &oldUri, const QString &newUri); void onUpdateDirectoryRequest(); void clearChildren(); protected: /*! * \brief getChildFromUri * \param uri * \return child item * \note * This is ususally used when fileCreated() and fileDeleted() happend, * and item must has parent item. */ FileItem *getChildFromUri(QString uri); /*! * \brief updateInfoSync *
* Update the item info synchously. *
* \note * This is ususally used when fileCreated() and fileDeleted() happend, * and item must has parent item. */ void updateInfoSync(); /*! * \brief updateInfoAsync *
* Update the item info asynchously. *
* This is ususally used when fileCreated() and fileDeleted() happend, * and item must has parent item. */ void updateInfoAsync(); void removeChildren(); private: FileItem *m_parent = nullptr; std::shared_ptr m_info; QVector *m_children = nullptr; FileItemModel *m_model = nullptr; bool m_expanded = false; std::shared_ptr m_watcher = nullptr; std::shared_ptr m_thumbnail_watcher = nullptr; QStringList m_ending_uris; QStringList m_waiting_add_queue; QStringList m_uris_to_be_removed; QTimer *m_idle = nullptr; /*! * \brief m_async_count *
* when enumerate children finished, we start a async job for update children info. * this count is record the current last un-updated children count. * while all job finished, the count will clear, and we can insert the rows to model. *
*/ int m_async_count = 0; /*! * \brief m_backend_enumerator * \note * only used in directory not support monitor. */ FileEnumerator *m_backend_enumerator; }; } #endif // FILEITEM_H peony/libpeony-qt/model/side-bar-single-item.cpp0000644000175000017500000000373514205101223020576 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2021, 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 . * * Authors: Yue Lan * */ #include "side-bar-single-item.h" #include "side-bar-model.h" #include "file-info.h" #include "file-info-job.h" using namespace Peony; SideBarSingleItem::SideBarSingleItem(const QString &uri, const QString &iconName, const QString &displayName, SideBarModel *model, QObject *parent) : SideBarAbstractItem(model, parent), m_uri(uri), m_icon_name(iconName), m_display_name(displayName) { m_info = FileInfo::fromUri(m_uri); } SideBarSingleItem::SideBarSingleItem(const QString &uri, SideBarModel *model, QObject *parent) : SideBarAbstractItem(model, parent) { m_uri = uri; m_info = FileInfo::fromUri(m_uri); if (m_info.get()->isEmptyInfo()) { FileInfoJob j(m_info); j.querySync(); } m_display_name = m_info.get()->displayName(); m_icon_name = m_info.get()->iconName(); } QString SideBarSingleItem::uri() { return m_uri; } QString SideBarSingleItem::displayName() { return m_display_name; } QString SideBarSingleItem::iconName() { return m_icon_name; } QModelIndex SideBarSingleItem::firstColumnIndex() { return m_model->firstColumnIndex(this); } QModelIndex SideBarSingleItem::lastColumnIndex() { return m_model->lastColumnIndex(this); } peony/libpeony-qt/model/side-bar-favorite-item.cpp0000644000175000017500000002045214205115226021137 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "side-bar-favorite-item.h" #include "side-bar-model.h" #include "file-utils.h" #include "file-info.h" #include "file-info-job.h" #include "bookmark-manager.h" #include #include #include using namespace Peony; bool kydroidInstall = false; QString kydroidPath = "kydroid:///"; static const char* localFileSystemPath = "file://"; SideBarFavoriteItem::SideBarFavoriteItem(QString uri,SideBarFavoriteItem *parentItem, SideBarModel *model, QObject *parent) : SideBarAbstractItem (model, parent),m_parent(parentItem) { m_uri = uri; m_is_root_child = m_parent == nullptr; if (m_is_root_child) { initChildren(); return; } //FIXME: replace BLOCKING api in ui thread. GFile* file = g_file_new_for_uri(m_uri.toUtf8().constData()); if (nullptr != file) { GFileInfo* fileInfo = g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); if (nullptr != fileInfo) { const char* displayName = g_file_info_get_attribute_string(fileInfo, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME); if (nullptr != displayName) { m_displayName = displayName; } g_object_unref(fileInfo); } g_object_unref(file); } if (m_displayName.isEmpty() || "" == m_displayName) { m_displayName = FileUtils::getFileDisplayName(m_uri); } m_iconName = FileUtils::getFileIconName(m_uri); m_info = FileInfo::fromUri(m_uri); auto infoJob = new FileInfoJob(m_info); infoJob->setAutoDelete(); connect(infoJob, &FileInfoJob::queryAsyncFinished, this, [=](){ Q_EMIT this->queryInfoFinished(); }); infoJob->queryAsync(); } void SideBarFavoriteItem::initChildren() { m_uri = "favorite:///"; m_displayName = tr("Favorite"); QString desktopUri = localFileSystemPath + QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); QString videoUri = localFileSystemPath + QStandardPaths::writableLocation(QStandardPaths::MoviesLocation); QString pictureUri = localFileSystemPath + QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); QString downloadUri = localFileSystemPath + QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); QString musicUri = localFileSystemPath + QStandardPaths::writableLocation(QStandardPaths::MusicLocation); QString docUri = localFileSystemPath + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); auto desktopItem = new SideBarFavoriteItem(desktopUri, this, m_model); auto trashItem = new SideBarFavoriteItem("trash:///", this, m_model); auto videoItem = new SideBarFavoriteItem(videoUri, this, m_model); auto pictureItem = new SideBarFavoriteItem(pictureUri, this, m_model); auto downloadItem = new SideBarFavoriteItem(downloadUri, this, m_model); auto musicItem = new SideBarFavoriteItem(musicUri, this, m_model); auto docItem = new SideBarFavoriteItem(docUri, this, m_model); m_children->append(desktopItem); m_children->append(docItem); m_children->append(musicItem); m_children->append(downloadItem); m_children->append(pictureItem); m_children->append(videoItem); m_children->append(trashItem); if (FileUtils::isFileExsit("file:///data/usershare")) { m_children->append(new SideBarFavoriteItem("favorite:///data/usershare?schema=file", this, m_model)); } // check kydroid is install if (FileUtils::isFileExsit("file:///var/lib/kydroid") || FileUtils::isFileExsit("file:///var/lib/kmre")) { GVfs* vfs = g_vfs_get_default(); if (vfs) { const gchar* const* schemas = g_vfs_get_supported_uri_schemes (vfs); if (schemas) { int i = 0; for (; schemas[i] != NULL; ++i) { if (0 == strcmp(schemas[i], "kydroid")) { kydroidInstall = true; kydroidPath = "kydroid:///"; break; } else if(0 == strcmp(schemas[i], "kmre")) { kydroidInstall = true; kydroidPath = "kmre:///"; break; } } } } if (kydroidInstall) m_children->append(new SideBarFavoriteItem(kydroidPath, this, m_model)); } m_model->insertRows(0, m_children->count(), firstColumnIndex()); //TODO: support custom bookmarks. auto bookmark = BookMarkManager::getInstance(); if (bookmark->isLoaded()) { syncBookMark(); } else { connect(bookmark, &BookMarkManager::urisLoaded, this, [=]() { syncBookMark(); disconnect(bookmark, &BookMarkManager::urisLoaded, this, nullptr); }); } } SideBarAbstractItem::Type SideBarFavoriteItem::type() { return SideBarAbstractItem::FavoriteItem; } QString SideBarFavoriteItem::uri() { return m_uri; } QString SideBarFavoriteItem::displayName() { if (!m_info) return m_displayName; return m_info.get()->displayName(); } QString SideBarFavoriteItem::iconName() { if (!m_info) return m_iconName; return m_info.get()->iconName(); } bool SideBarFavoriteItem::hasChildren() { return m_is_root_child; } QModelIndex SideBarFavoriteItem::firstColumnIndex() { //TODO: bind with model return m_model->firstColumnIndex(this); } QModelIndex SideBarFavoriteItem::lastColumnIndex() { //TODO: bind with model return m_model->lastColumnIndex(this); } void SideBarFavoriteItem::syncBookMark() { qDebug()<<"sync book mark=================="<displayName(); auto bookmark = BookMarkManager::getInstance(); auto uris = bookmark->getCurrentUris(); for (auto uri : uris) { auto item = new SideBarFavoriteItem(uri, this, m_model); *m_children<insertRows(m_children->count() - 1, 1, this->firstColumnIndex()); } connect(bookmark, &BookMarkManager::bookMarkAdded, this, [=](const QString &uri, bool successed) { if (successed) { auto item = new SideBarFavoriteItem(FileUtils::urlDecode(uri), this, m_model); *m_children<insertRows(m_children->count() - 1, 1, this->firstColumnIndex()); } }); connect(bookmark, &BookMarkManager::bookMarkRemoved, this, [=] (const QString &uri, bool successed) { QString delUri = getTargetUri(uri); if (successed) { for (auto item : *m_children) { QString itemUri = getTargetUri(item->uri()); if (delUri == itemUri) { m_model->removeRow(m_children->indexOf(item), this->firstColumnIndex()); m_children->removeOne(item); } } } }); } QString SideBarFavoriteItem::getTargetUri(const QString &uri) { QString sguri = uri; if (uri.startsWith("favorite://")) { g_autoptr(GFile) turi = g_file_new_for_uri(FileUtils::urlEncode(uri).toUtf8().constData()); if (turi) { g_autoptr(GFileInfo) turiInfo = g_file_query_info(turi, "standard::*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); if (turiInfo) { g_autofree char* tturi = g_file_info_get_attribute_as_string(turiInfo, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); if (tturi) { sguri = tturi; } } } } return FileUtils::urlDecode(sguri); } peony/libpeony-qt/model/path-completer.h0000644000175000017500000000365414205101223017266 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef PATHCOMPLETER_H #define PATHCOMPLETER_H #include "peony-core_global.h" #include namespace Peony { /*! * \brief The PathCompleter class * \details * This class is used to complete the path based on gvfs. * Note that the peony's file system model is desgining-different from * QFileSystemModel, so that the QCompleter should aslo do some special * treatments. * PathCompleter binds with a derived QStringListModel, the PathModel. * This model dynamiclly changes its contents when PathCompleter changed * its split string. The most cases of that is the line edit which bind with * a completer changed its texts. Completer will trigger the splitPath() * method, then the model will decided if its contents need be refereshed. * \see PathModel */ class PEONYCORESHARED_EXPORT PathCompleter : public QCompleter { Q_OBJECT public: explicit PathCompleter(QObject *parent = nullptr); explicit PathCompleter(QAbstractItemModel *model, QObject *parent = nullptr); protected: QStringList splitPath(const QString &path) const override; QString pathFromIndex(const QModelIndex &index) const override; }; } #endif // PATHCOMPLETER_H peony/libpeony-qt/model/file-item-proxy-filter-sort-model.cpp0000644000175000017500000005266314205115226023311 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * Authors: Meihong He * */ #include "file-item-model.h" #include "file-item.h" #include "file-item-proxy-filter-sort-model.h" #include "file-info.h" #include "file-meta-info.h" #include "file-label-model.h" #include "file-utils.h" #include "file-operation-utils.h" #include "global-settings.h" #include #include #include #include #include using namespace Peony; QLocale locale = QLocale(QLocale::system().name()); QCollator comparer = QCollator(locale); FileItemProxyFilterSortModel::FileItemProxyFilterSortModel(QObject *parent) : QSortFilterProxyModel(parent) { setDynamicSortFilter(false); //enable number sort, like 100 is after 99 comparer.setNumericMode(true); auto settings = GlobalSettings::getInstance(); m_show_hidden = settings->isExist(SHOW_HIDDEN_PREFERENCE)? settings->getValue(SHOW_HIDDEN_PREFERENCE).toBool(): false; connect(GlobalSettings::getInstance(), &GlobalSettings::valueChanged, this, [=] (const QString& key) { if (SHOW_HIDDEN_PREFERENCE == key) { m_show_hidden= GlobalSettings::getInstance()->getValue(key).toBool(); invalidateFilter(); } }); m_use_default_name_sort_order = settings->isExist(SORT_CHINESE_FIRST)? settings->getValue(SORT_CHINESE_FIRST).toBool(): false; m_folder_first = settings->isExist(SORT_FOLDER_FIRST)? settings->getValue(SORT_FOLDER_FIRST).toBool(): true; } void FileItemProxyFilterSortModel::setSourceModel(QAbstractItemModel *model) { if (sourceModel()) disconnect(sourceModel()); QSortFilterProxyModel::setSourceModel(model); FileItemModel *file_item_model = static_cast(model); //connect(file_item_model, &FileItemModel::updated, this, &FileItemProxyFilterSortModel::update); } FileItem *FileItemProxyFilterSortModel::itemFromIndex(const QModelIndex &proxyIndex) { FileItemModel *model = static_cast(sourceModel()); QModelIndex index = mapToSource(proxyIndex); return model->itemFromIndex(index); } QModelIndex FileItemProxyFilterSortModel::getSourceIndex(const QModelIndex &proxyIndex) { return mapToSource(proxyIndex); } const QModelIndex FileItemProxyFilterSortModel::indexFromUri(const QString &uri) { FileItemModel *model = static_cast(sourceModel()); const QModelIndex sourceIndex = model->indexFromUri(uri); return mapFromSource(sourceIndex); } bool FileItemProxyFilterSortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { //comment these improve code to fix disorder issue //and chinese first or folder first has not effect issue //Fix me with better solutions // if (left.data().isNull()) // return sortOrder() == Qt::AscendingOrder? false: true; // if (right.data().isNull()) // return sortOrder() == Qt::AscendingOrder? true: false; // if (left.column() != 0 || right.column() != 0) { // return true; // } //qDebug()<(sourceModel()); auto leftItem = model->itemFromIndex(left); auto rightItem = model->itemFromIndex(right); if (!(leftItem->hasChildren() && rightItem->hasChildren())) { //make folder always has a higher order. if (!leftItem->hasChildren() && !rightItem->hasChildren()) { goto default_sort; } if (m_folder_first) { bool lesser = leftItem->hasChildren(); if (sortOrder() == Qt::AscendingOrder) return lesser; return !lesser; } } default_sort: switch (sortColumn()) { case FileItemModel::FileName: { if (FileOperationUtils::leftNameIsDuplicatedFileOfRightName(leftItem->m_info->displayName(), rightItem->m_info->displayName())) { return FileOperationUtils::leftNameLesserThanRightName(leftItem->info()->displayName(), rightItem->info()->displayName()); } if (m_use_default_name_sort_order) { QString leftDisplayName = leftItem->m_info->displayName(); QString rightDisplayName = rightItem->m_info->displayName(); //fix chinese first sort wrong issue, link to bug#70836 if(startWithChinese(leftDisplayName) && ! startWithChinese(rightDisplayName)) return false; else if(! startWithChinese(leftDisplayName) && startWithChinese(rightDisplayName)) return true; else return comparer.compare(leftDisplayName, rightDisplayName) < 0; } //return leftItem->m_info->displayName().toLower() < rightItem->m_info->displayName().toLower(); return comparer.compare(leftItem->m_info->displayName(), rightItem->m_info->displayName()) < 0; } case FileItemModel::FileSize: { return leftItem->m_info->size() < rightItem->m_info->size(); } case FileItemModel::FileType: { return leftItem->m_info->fileType() < rightItem->m_info->fileType(); } case FileItemModel::ModifiedDate: { //delete time sort in trash, fix bug#63093 if (leftItem->uri().startsWith("trash://")) return leftItem->m_info->deletionDate() < rightItem->m_info->deletionDate(); return leftItem->m_info->modifiedTime() < rightItem->m_info->modifiedTime(); } default: break; } } return QSortFilterProxyModel::lessThan(left, right); } bool FileItemProxyFilterSortModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { //FIXME: FileItemModel *model = static_cast(sourceModel()); //root auto childIndex = model->index(sourceRow, 0, sourceParent); if (childIndex.isValid()) { auto item = static_cast(childIndex.internalPointer()); if(!item->shouldShow()) return false; if (!m_show_hidden) { //qDebug()<m_info->displayName()<rowCount(sourceParent); //QMessageBox::warning(nullptr, "filter", item->m_info->displayName()); //qDebug()<m_info->displayName(); if (item->m_info->displayName() != nullptr) { if (item->m_info->displayName().at(0) == '.') //qDebug()<m_info->displayName()<rowCount(sourceParent); return false; } } //regExp //special Volumn of 839 M upgrade part not show process auto targetUri = FileUtils::getTargetUri(item->uri()); if (targetUri == "") targetUri = item->uri(); if (targetUri.startsWith("file:///media/") && targetUri.endsWith("/2691-6AB8")) return false; //FIXME use display name to hide 839 MB disk if (item->m_info->displayName().contains("839 MB")) return false; //check the file info filter conditions //qDebug()<<"start filter conditions check"<m_info->displayName()<m_info->type(); if (! checkFileTypeFilter(item->m_info->type())) return false; if (! checkFileModifyTimeFilter(item->m_info->modifiedTime())) return false; if (! checkFileSizeOrTypeFilter(item->m_info->size(), item->m_info->isDir())) return false; if (! checkFileNameFilter(item->m_info->displayName())) return false; //check the file label filter conditions if (m_label_name != "" || m_label_color != Qt::transparent) { QString uri = item->m_info->uri(); if (m_label_name != "") { auto names = FileLabelModel::getGlobalModel()->getFileLabels(uri); if (! names.contains(m_label_name)) return false; } if (m_label_color != Qt::transparent) { auto colors = FileLabelModel::getGlobalModel()->getFileColors(uri); if (! colors.contains(m_label_color)) return false; } } //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) { bool bfind = false; QString uri = item->m_info->uri(); if (m_show_label_names.size() >0 ) { auto names = FileLabelModel::getGlobalModel()->getFileLabels(uri); for(auto temp : m_show_label_names) { if(names.contains(temp)) { bfind = true; break; } } } if (! bfind && m_show_label_colors.size() >0) { auto colors = FileLabelModel::getGlobalModel()->getFileColors(uri); for(auto temp : m_show_label_colors) { if (colors.contains(temp)) { bfind = true; break; } } } if (! bfind) return false; } //check the blur name, can use as search color labels if (m_blur_name != "") { QString uri = item->m_info->uri(); auto names = FileLabelModel::getGlobalModel()->getFileLabels(uri); bool find = false; for(auto temp : names) { if ((m_case_sensitive && temp.indexOf(m_blur_name) >= 0) || (! m_case_sensitive && temp.toLower().indexOf(m_blur_name.toLower()) >= 0)) { find = true; break; } } if (! find) return false; } } return true; } bool FileItemProxyFilterSortModel::checkFileNameFilter(const QString &displayName) const { if (m_file_name_list.size() == 0) return true; for(auto key:m_file_name_list) { if (displayName.contains(key)) return true; } return false; } bool FileItemProxyFilterSortModel::checkFileTypeFilter(QString type) const { //qDebug()<<"m_show_file_type: "< totalTypeList; if (m_file_type_list.count() > 0) totalTypeList.append(m_file_type_list); if (! totalTypeList.contains(m_show_file_type) && m_show_file_type != ALL_FILE) totalTypeList.append(m_show_file_type); for(int i=0; i md_year) return true; break; } default: break; } } return false; } bool FileItemProxyFilterSortModel::checkFileSizeFilter(quint64 size) const { //qDebug()<<"checkFileSizeFilter: "< 0 &&size <=16 * K_BASE) return true; break; } case SMALL: //(16k-1M] { if(size > 16 * K_BASE && size <=K_BASE * K_BASE) return true; break; } case MEDIUM: //(1M-128M] { if(size > K_BASE * K_BASE && size <= 128 * K_BASE * K_BASE) return true; break; } case BIG: //(128M-1G] { if(size > 128 * K_BASE * K_BASE && size <= K_BASE * K_BASE * K_BASE) return true; break; } case LARGE: //(1-4G] { if (size > K_BASE * K_BASE * K_BASE&& size <= 4*K_BASE * K_BASE * K_BASE) return true; break; } case GREAT: //>4G { if (size > 4*K_BASE * K_BASE * K_BASE) return true; break; } default: break; } } return false; } bool FileItemProxyFilterSortModel::checkFileSizeOrTypeFilter(quint64 size, bool isDir) const { if (m_file_size_list.count() == 0 || m_file_size_list.contains(ALL_FILE)) return true; else if(isDir) return false; return checkFileSizeFilter(size); } void FileItemProxyFilterSortModel::update() { invalidateFilter(); } void FileItemProxyFilterSortModel::setShowHidden(bool showHidden) { GlobalSettings::getInstance()->setGSettingValue(SHOW_HIDDEN_PREFERENCE, showHidden); m_show_hidden = showHidden; invalidateFilter(); } void FileItemProxyFilterSortModel::setUseDefaultNameSortOrder(bool use) { GlobalSettings::getInstance()->setValue(SORT_CHINESE_FIRST, use); m_use_default_name_sort_order = use; beginResetModel(); sort(sortColumn()>0? sortColumn(): 0, sortOrder()==Qt::DescendingOrder? Qt::DescendingOrder: Qt::AscendingOrder); endResetModel(); } void FileItemProxyFilterSortModel::setFolderFirst(bool folderFirst) { GlobalSettings::getInstance()->setValue(SORT_FOLDER_FIRST, folderFirst); m_folder_first = folderFirst; beginResetModel(); sort(sortColumn()>0? sortColumn(): 0, sortOrder()==Qt::DescendingOrder? Qt::DescendingOrder: Qt::AscendingOrder); endResetModel(); } void FileItemProxyFilterSortModel::addFileNameFilter(QString key, bool updateNow) { m_file_name_list.append(key); if (updateNow) invalidateFilter(); } void FileItemProxyFilterSortModel::addFilterCondition(int option, int classify, bool updateNow) { switch (option) { case 0: if (! m_file_type_list.contains(classify)) m_file_type_list.append(classify); break; case 1: if (! m_file_size_list.contains(classify)) m_file_size_list.append(classify); break; case 2: if (! m_modify_time_list.contains(classify)) m_modify_time_list.append(classify); break; default: break; } if (updateNow) invalidateFilter(); } void FileItemProxyFilterSortModel::removeFilterCondition(int option, int classify, bool updateNow) { switch (option) { case 1: if (! m_file_type_list.contains(classify)) m_file_type_list.removeOne(classify); break; case 2: if (! m_modify_time_list.contains(classify)) m_modify_time_list.removeOne(classify); break; case 3: if (! m_file_size_list.contains(classify)) m_file_size_list.removeOne(classify); break; default: break; } if (updateNow) invalidateFilter(); } void FileItemProxyFilterSortModel::clearConditions() { m_file_name_list.clear(); m_file_type_list.clear(); m_file_size_list.clear(); m_modify_time_list.clear(); } void FileItemProxyFilterSortModel::setFilterConditions(int fileType, int modifyTime, int fileSize) { m_show_file_type = fileType; m_show_file_size = fileSize; m_show_modify_time = modifyTime; invalidateFilter(); } void FileItemProxyFilterSortModel::setFilterLabelConditions(QString name, QColor color) { m_label_name = name; m_label_color = color; invalidateFilter(); } void FileItemProxyFilterSortModel::setMutipleLabelConditions(QStringList names, QList colors) { m_show_label_names.clear(); m_show_label_colors.clear(); for(auto name : names) { m_show_label_names.append(name); } for(auto color : colors) { m_show_label_colors.append(color); } invalidateFilter(); } void FileItemProxyFilterSortModel::setLabelBlurName(QString blurName, bool caseSensitive) { m_blur_name = blurName; m_case_sensitive = caseSensitive; invalidateFilter(); } bool FileItemProxyFilterSortModel::startWithChinese(const QString &displayName) const { //NOTE: a newly created file might could not get display name soon. if (displayName.isEmpty()) { return false; } auto firstStrUnicode = displayName.at(0).unicode(); return (firstStrUnicode <=0x9FA5 && firstStrUnicode >= 0x4E00); } QModelIndexList FileItemProxyFilterSortModel::getAllFileIndexes() { //FIXME: how about the tree? 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(i, 0, QModelIndex()).data(FileItemModel::UriRole).toString(); disyplayName = FileUtils::getFileDisplayName(uri); } if (!disyplayName.startsWith(".")) { l<. * * Authors: Yue Lan * */ #ifndef SIDEBARABSTRACTITEM_H #define SIDEBARABSTRACTITEM_H #include #include #include "peony-core_global.h" #include #include namespace Peony { class SideBarModel; class FileInfo; class PEONYCORESHARED_EXPORT SideBarAbstractItem : public QObject { friend class SideBarFileSystemItem; friend class SideBarModel; Q_OBJECT public: enum Type { FavoriteItem, PersonalItem, FileSystemItem, NetWorkItem, SeparatorItem, VFSItem, SingleItem }; explicit SideBarAbstractItem(SideBarModel* model, QObject *parent = nullptr); virtual ~SideBarAbstractItem(); virtual Type type() = 0; virtual QString uri(){ return m_uri; } virtual QString displayName(){ return m_displayName; } virtual QString iconName(){ return m_iconName; } virtual bool isRemoveable(){ return m_removeable; } virtual bool isEjectable() { return m_ejectable; } virtual bool isStopable() { return m_stopable; } virtual bool isMountable() { return m_mountable; } virtual bool isUnmountable(){ return m_unmountable; } virtual bool isMounted() { return m_mounted; } virtual QString getDevice() { return m_device; } virtual bool hasChildren() = 0; virtual QModelIndex firstColumnIndex() = 0; virtual QModelIndex lastColumnIndex() = 0; virtual SideBarAbstractItem *parent() = 0; virtual bool filterShowRow(){ return true; } protected: QVector *m_children = nullptr; SideBarModel *m_model = nullptr; std::shared_ptr m_info; QString m_uri; QString m_displayName; QString m_iconName; QString m_device; QString m_mountPoint; bool m_removeable = false; bool m_ejectable = false; bool m_stopable = false; bool m_mountable = false; bool m_unmountable = false; bool m_mounted = false; Q_SIGNALS: void queryInfoFinished(); void findChildrenFinished(); void updated(); public Q_SLOTS: virtual void onUpdated() = 0; virtual void eject(GMountUnmountFlags ejectFlag) = 0; virtual void unmount() = 0; virtual void format() = 0; virtual void ejectOrUnmount() {} virtual void mount(){} virtual void findChildren() = 0; virtual void findChildrenAsync() = 0; virtual void clearChildren(); }; } #endif // SIDEBARABSTRACTITEM_H peony/libpeony-qt/model/README.md0000644000175000017500000000352414205101223015444 0ustar fengfeng# Model and ModelItem As we should display a large number element in the file manager's view, we should not handle them directly. Model is desgin to help us doing that. There are 2 main types model item in peony. Item in directory view and item in side bar. ## FileItem FileItem is the absctract item class contract with FileItemModel. The different from FileInfo to FileItem is that FileItem has concept of children and parent. This makes FileItem instance has a tree struction and can represent a tree item in a view(non-tree as well). Other different is that FileItem instance is not shared. You can hold many FileItem instances crosponding to the same FileInfo, but they are allocated in their own memory space. Every FileItem instance which has children will aslo support monitoring. When find the children of the item, it will start a monitor for this directory. ## FileItemModel FileItemModel is the model of FileItem. This class supplies the common interface to QAbstractItemView based view classes. A FileItem instance must bind to a model instance, so that it could tell the view how to show its or its children's data through the model. You can learn more model/view programming at Qt's official document and example. ## FileItemProxyFilterSortModel This class is use for sorting and filtering the FileItemModel instance. Every proxy model have a mapping to a FileItemModel. FileItemModel is disordered and non-filterable, that means we can't sort the files by name or other order and filter the hidden files. With this interface, it's much easier to implement the sorting and filtering. ## item in side bar Item in side bar is similar to item in directory view. The main differences between 2 items are: - side bar item is extensionable by impling SideBarAbstractItem interface. - side bar item is more lightweight, it doesn't hold a FileInfo instance.peony/libpeony-qt/model/path-completer.cpp0000644000175000017500000000346514205101223017621 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "path-completer.h" #include "path-bar-model.h" #include using namespace Peony; PathCompleter::PathCompleter(QObject *parent) : QCompleter(parent) { } PathCompleter::PathCompleter(QAbstractItemModel *model, QObject *parent) : QCompleter(parent) { Q_UNUSED(model); } QStringList PathCompleter::splitPath(const QString &path) const { // QAbstractItemModel *m = model(); // PathBarModel* model = static_cast(m); // auto l = model->stringList(); // if (path.endsWith("/")) { // model->setRootUri(path); // } else { // QString tmp0 = path; // QString tmp = path; // tmp.chop(path.size() - path.lastIndexOf("/")); // if (tmp.endsWith("/")) { // tmp.append("/"); // } // model->setRootUri(tmp); // } // auto splitPath = QCompleter::splitPath(path); return QCompleter::splitPath(path); } QString PathCompleter::pathFromIndex(const QModelIndex &index) const { //qDebug()<. * * Authors: Yue Lan * */ #ifndef FILEITEMMODEL_H #define FILEITEMMODEL_H #include #include "peony-core_global.h" namespace Peony { class FileItem; class FileItemProxyFilterSortModel; /*! * \brief The FileItemModel class *
* FileItemModel is the model of FileItem. For now, this class doesn't hold and * manage the data. This class is just supply the common interface to * QAbstractItemView based classes. FileItem must bind to a model instance, so * that it could tell the view how to show its or its children's data through * the model. *
* \bug * If we setRootItem() too frequently, there is a certain cause of crash. * I guess it is because the glib async callback call after the user data (enumerator) * has been deleted. I have no idea how to fix this bug now, maybe set a restriction * of frequency of model root change in UI is one solution. */ class PEONYCORESHARED_EXPORT FileItemModel : public QAbstractItemModel { friend class FileItem; friend class FileItemProxyFilterSortModel; Q_OBJECT public: enum ColumnType { FileName, ModifiedDate, FileType, FileSize, Owner, Other }; Q_ENUM(ColumnType) enum ItemRole { UriRole = Qt::UserRole }; Q_ENUM(ItemRole) explicit FileItemModel(QObject *parent = nullptr); ~FileItemModel() override; /*! * \brief setPositiveResponse * \param positive * \details * FileItem provide 2 kinds of data query type for find children, * positive and inpositive. The positive query will report children * were found when the FileEnumerator::childrenUpdated() signal emitted, * this signal take a string list of uris enumerator found at that time ( * usually a batch of async enumeration callbacked results). * and the inpositive query will report children being found the same time * enumerationFinished() emitted, and use FileEnumerator::getChildren() get * all children at once. * * The default positive type is inpositive. */ void setPositiveResponse(bool positive = true) { m_is_positive = positive; } /*! * \brief isPositiveResponse * \return * \see setPositiveResponse() */ bool isPositiveResponse() { return m_is_positive; } void setExpandable(bool expandable) { m_can_expand = expandable; } bool canExpandChildren() { return m_can_expand; } const QString getRootUri(); void setRootUri(const QString &uri); /*! * \brief setRootItem * \param item, the directory should be shown in view. *
* once setRootItem() is called, the current root and it children will be delete, * it will start a new enumerating and monitoring for new item. *
*/ void setRootItem(FileItem *item); /*! * \brief itemFromIndex * \param index * \return * \retval the item instance of index at model. */ FileItem *itemFromIndex(const QModelIndex &index) const; /*! * \brief firstColumnIndex * \param item * \return * \retval the first column index(FileName) which crosponding this item. * \note Every index's internal data at same row is the same item. */ QModelIndex firstColumnIndex(FileItem *item); /*! * \brief lastColumnIndex * \param item * \return * \retval the last column index which crosponding this item. * \note Every index's internal data at same row is the same item. */ QModelIndex lastColumnIndex(FileItem *item); const QModelIndex indexFromUri(const QString &uri); QModelIndex index(int row, int column, const QModelIndex &parent) const override; QModelIndex parent(const QModelIndex &child) const override; int rowCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; bool hasChildren(const QModelIndex &parent) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; /*! * \brief canFetchMore * \param parent * \return * \retval true if tree view item has children and first expanded. * \retval false *
* QAbstractItemModel provide a lazy populate interface. * canFetchMore() is most happening in tree view. If a parent index has children, * hasChildren() should return true. Then tree view will set this parent index expandable. * if we expand the item, canFetchMore() will be called. we can dynamicly load our data after * set this method return true. After loading finished, we must set this method return fasle, * for telling the view data loading is done. *
*
* This desgin has a small defect. Once we expanded a parent index, * the data will not free until we delete them manually. * I know using custom view and model with a custom signal-control-mechanism can solve this problem, * but I don't want to let my model become sexceptional. * Just like QFileSystemModel does, I want my model perform well at all kinds of View, * whatever List, Icon, Tree or others. *
*/ bool canFetchMore(const QModelIndex &parent) const override; /*! * \brief fetchMore * \param parent *
* This method will shcedule when a parent index canFetchMore. * In this method, we usually load our data, and call beginInsertRows * to tell how many rows we has added into this index. * Then we need call endInsertRows(), and return. Actually, fetchMore just * tell model the new rowCount of parent index to control showing our newly data. * newly data will be shown after data() called, as same as other non-delayed data. *
*/ void fetchMore(const QModelIndex &parent) override; // Add data: bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex()) override; // Remove data: bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex()) 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; void sendPathChangeRequest(const QString &uri); Q_SIGNALS: /*! * \brief findChildrenStarted *
* This signal is use for telling other object that the item has started enumerating. * An icon view(or list view) can connect this signal for setting cursor as style-loading. *
* \see findChildrenFinished() */ void findChildrenStarted(); /*! * \brief findChildrenFinished *
* This signal is use for telling other object that the item has finished loading. * An icon view(or list view) can connect this signal for setting cursor as normal. *
* \see findChildrenStarted(). */ void findChildrenFinished(); /*! * \brief updated *
* when a 'folder' item children changed, this signal should be emit. *
* \note proxy model should connect this signal and start sort and filter again. */ void updated(); void selectRequest(const QStringList &uris); void changePathRequest(const QString &uri, bool addHistory, bool forceUpdate); public Q_SLOTS: /*! * \brief onFoundChildren * \param parent * \deprecated */ void onFoundChildren(const QModelIndex &parent); /*! * \brief onItemAdded * \param item * \deprecated */ void onItemAdded(FileItem *item); /*! * \brief onItemRemoved * \param item * \deprecated */ void onItemRemoved(FileItem *item); void cancelFindChildren(); void setRootIndex(const QModelIndex &index); private: FileItem *m_root_item = nullptr; bool m_is_positive = false; bool m_can_expand = false; QString m_root_uri = "file:///"; }; } #endif // FILEITEMMODEL_H peony/libpeony-qt/connect-to-server-dialog.cpp0000644000175000017500000004726714205115226020427 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Ding Jing * */ #include "connect-to-server-dialog.h" #include "global-settings.h" #include #include #include #include #include #include #include #include #include using namespace Peony; static const QString ftpTypeStr="ftp"; static const QString sftpTypeStr="sftp"; static const QString sambaTypeStr="samba"; static const QString ftpDefaultPortStr="21"; static const QString sftpDefaultPortStr="22"; static const QString sambaDefaultPortStr="445"; static QString passwdEncode (QString p); static QString passwdDecode (QString p); ConnectServerDialog::ConnectServerDialog(QWidget *parent) : QDialog(parent) { setFixedSize(m_widget_size); setWindowIcon(QIcon::fromTheme("network-server")); setWindowTitle(tr("connect to server")); setBackgroundRole(QPalette::Base); setAutoFillBackground(true); m_main_layout = new QVBoxLayout(this); m_remote_type_edit = new QComboBox; m_remote_type_label = new QLabel; m_ip_label = new QLabel; m_port_label = new QLabel; m_ip_edit = new QLineEdit; m_port_editor = new QComboBox; m_remote_layout = new QGridLayout; m_remote_type = new QStringList; m_remote_port = new QStringList; m_main_layout->addSpacing(12); m_remote_type->append(ftpTypeStr); m_remote_type->append(sftpTypeStr); m_remote_type->append(sambaTypeStr); m_remote_port->append("20"); m_remote_port->append(ftpDefaultPortStr); m_remote_port->append(sftpDefaultPortStr); m_remote_port->append(sambaDefaultPortStr); m_ip_label->setText(tr("ip")); m_port_editor->setEditable(true); m_port_label->setText(tr("port")); m_remote_type_label->setText(tr("type")); m_main_layout->setMargin(m_widget_margin); m_remote_type_edit->setAutoCompletion(true); m_ip_label->setFixedHeight(36); m_ip_edit->setFixedHeight(36); m_ip_edit->setFixedWidth(194); m_port_label->setFixedHeight(36); m_port_editor->setFixedHeight(36); m_port_editor->setFixedWidth(65); m_remote_type_label->setFixedHeight(36); m_remote_type_edit->setFixedHeight(36); m_remote_type_edit->addItems(*m_remote_type); m_port_editor->addItems(*m_remote_port); m_remote_type_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); m_remote_layout->addWidget(m_remote_type_label, 0, 0, 1, 1); m_remote_layout->addWidget(m_remote_type_edit, 0, 1, 1, 5); m_remote_layout->setVerticalSpacing(20); m_remote_layout->addWidget(m_ip_label, 1, 0, 1, 1); m_remote_layout->addWidget(m_ip_edit, 1, 1, 1, 1); m_remote_layout->addWidget(m_port_label, 1, 2, 1, 1); m_remote_layout->addWidget(m_port_editor, 1, 3, 1, 3); m_main_layout->addLayout(m_remote_layout); m_main_layout->addSpacing(28); m_favorite_label = new QLabel; m_favorite_layout = new QVBoxLayout; m_favorite_list = new QListWidget; m_favorite_list->setFixedSize(m_favorite_list_size); m_favorite_label->setText(tr("Personal Collection server:")); m_favorite_layout->addWidget(m_favorite_label); m_favorite_layout->addWidget(m_favorite_list); m_main_layout->addLayout(m_favorite_layout); m_btn_add = new QPushButton; m_btn_del = new QPushButton; m_btn_conn = new QPushButton; m_btn_layout = new QHBoxLayout; m_main_layout->addSpacing(12); m_btn_add->setText(tr("add")); m_btn_del->setText(tr("delete")); m_btn_conn->setText(tr("connect")); m_btn_layout->addWidget(m_btn_add); m_btn_layout->addWidget(m_btn_del); m_btn_layout->addSpacing(72); m_btn_layout->addWidget(m_btn_conn); m_btn_conn->setAutoDefault(true); m_btn_add->setAutoDefault(false); m_btn_del->setAutoDefault(false); m_main_layout->addLayout(m_btn_layout); setLayout(m_main_layout); if (GlobalSettings::getInstance()->isExist(REMOTE_SERVER_REMOTE_IP)) { QMap uriList = GlobalSettings::getInstance()->getValue(REMOTE_SERVER_REMOTE_IP).toMap(); for (auto uri = uriList.constBegin(); uri != uriList.constEnd(); ++uri) { QUrl url(uri.key ()); if ("" != uri.key ()) { QString urit = uri.key () == url.toDisplayString() ? uri.key() : url.toDisplayString(); QListWidgetItem* item = new QListWidgetItem; item->setText(urit); m_favorite_uri.insert (urit, uri.value ()); m_favorite_list->addItem(item); m_favorite_widgets.insert(urit, item); } } syncUri(); } connect(m_btn_add, &QPushButton::clicked, this, [=] (bool checked) { addUri(uri()); Q_UNUSED(checked); }); connect(m_remote_type_edit, &QComboBox::currentTextChanged, this, [=] (const QString& type) { if (sambaTypeStr == type.toLower()) { m_port_editor->setEditText(sambaDefaultPortStr); } else if (ftpTypeStr == type.toLower()) { m_port_editor->setEditText(ftpDefaultPortStr); }else if (sftpTypeStr == type.toLower()) { m_port_editor->setEditText(sftpDefaultPortStr); } }); Q_EMIT m_remote_type_edit->currentTextChanged(ftpTypeStr); connect(m_btn_del, &QPushButton::clicked, this, [=] (bool checked) { removeUri(uri()); if (m_favorite_uri.count() <= 0) { m_favorite_list->clear(); } else { setUri((m_favorite_uri.firstKey ())); m_favorite_list->setCurrentItem(m_favorite_widgets[m_favorite_uri.firstKey ()]); } m_favorite_list->update(); Q_UNUSED(checked); }); connect(m_favorite_list, &QListWidget::itemClicked, this, [=] (QListWidgetItem *item) { setUri(item->text()); }); connect(m_btn_conn, &QPushButton::clicked, this, [=] (bool checked) { if ("" != uri()) { accept(); } Q_UNUSED(checked); }); } ConnectServerDialog::~ConnectServerDialog() { } QString ConnectServerDialog::uri() { QString uuri = ""; if (m_remote_type_edit->currentText() == sambaTypeStr) { uuri = "smb://" + m_ip_edit->text() + ":" + m_port_editor->currentText(); } else if (m_remote_type_edit->currentText() == ftpTypeStr) { uuri = "ftp://" + m_ip_edit->text() + ":" + m_port_editor->currentText(); }else if (m_remote_type_edit->currentText() == sftpTypeStr) { uuri = "sftp://" + m_ip_edit->text() + ":" + m_port_editor->currentText(); } return uuri; } QString ConnectServerDialog::getIP() { return m_ip_edit->text(); } void ConnectServerDialog::syncUri() { GlobalSettings::getInstance()->setValue(REMOTE_SERVER_REMOTE_IP, m_favorite_uri); GlobalSettings::getInstance()->forceSync(REMOTE_SERVER_REMOTE_IP); } void ConnectServerDialog::setUri(QString uri) { QUrl rl = uri; QString port = QString::number(rl.port()); QString schema = rl.scheme(); if ("smb" == schema) { m_remote_type_edit->setCurrentText(sambaTypeStr); } else { m_remote_type_edit->setCurrentText(rl.scheme()); } m_ip_edit->setText(rl.host()); m_port_editor->setCurrentText(port); } void ConnectServerDialog::addUri(QString uri) { bool canInsert = false; QUrl url(uri); // fixme:// fix if (!m_favorite_uri.contains(uri) && !m_favorite_uri.contains(url.toDisplayString())) { canInsert = true; } if (canInsert) { m_favorite_uri.insert(url.toDisplayString(), QMap()); QListWidgetItem* item = new QListWidgetItem; item->setText(url.toDisplayString()); m_favorite_list->addItem(item); m_favorite_widgets.insert(url.toDisplayString(), item); m_favorite_list->setCurrentItem(item); syncUri(); GlobalSettings::getInstance()->slot_updateRemoteServer(url.toDisplayString(), true); } } void ConnectServerDialog::removeUri(QString uri) { QUrl url (uri); QString removeUrl = uri; if (m_favorite_uri.contains(uri)) { removeUrl = uri; } else if (m_favorite_uri.contains(url.toDisplayString())) { removeUrl = url.toDisplayString(); } if (m_favorite_uri.contains(removeUrl)) { m_favorite_uri.remove(removeUrl); syncUri(); GlobalSettings::getInstance()->slot_updateRemoteServer(removeUrl,false); } if (m_favorite_widgets.contains(removeUrl)) { QListWidgetItem* item = m_favorite_widgets[removeUrl]; m_favorite_list->removeItemWidget(item); m_favorite_widgets.remove(removeUrl); delete item; } } ConnectServerLogin::ConnectServerLogin(QString uri, QWidget *parent) : QDialog(parent),m_remoteIP(uri) { setFixedSize(m_widget_size); setWindowIcon(QIcon::fromTheme("network-server")); setWindowTitle(tr("The login user")); setBackgroundRole(QPalette::Base); setAutoFillBackground(true); m_main_layout = new QVBoxLayout(this); m_main_layout->addSpacing(12); m_main_layout->setMargin(m_widget_margin); QUrl url(uri); m_tip = new QLabel; m_tip->setWordWrap(true); m_tip->setText(QString(tr("Please enter the %1's user name and password of the server.")).arg(url.host ())); m_main_layout->addWidget(m_tip); m_usr_label = new QLabel; m_usr_btn_group = new QVBoxLayout; m_usr_btn_guest = new QRadioButton; m_usr_btn_usr = new QRadioButton; m_usr_layout = new QHBoxLayout; m_usr_label->setText(tr("User's identity")); m_usr_btn_guest->setText(tr("guest")); m_usr_btn_usr->setText(tr("Registered users")); m_usr_btn_group->addWidget(m_usr_btn_guest); m_usr_btn_group->addWidget(m_usr_btn_usr); m_usr_layout->addWidget(m_usr_label); m_usr_layout->addLayout(m_usr_btn_group); m_usr_label->setAlignment (Qt::AlignTop | Qt::AlignLeft); m_usr_layout->setAlignment(Qt::AlignTop | Qt::AlignLeft); m_main_layout->addLayout(m_usr_layout); m_reg_usr_name_label = new QLabel; m_reg_usr_passwd_label = new QLabel; m_reg_usr_name_editor = new QComboBox; m_reg_usr_passwd_editor = new QLineEdit; m_reg_usr_combox = new QCheckBox; m_reg_usr_layout = new QGridLayout; m_reg_usr_name_editor->setEditable (true); 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_name_label->setFixedHeight(36); m_reg_usr_passwd_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_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->setVerticalSpacing(12); m_main_layout->addLayout(m_reg_usr_layout); m_btn_cancel = new QPushButton; m_btn_ok = new QPushButton; m_btn_layout = new QHBoxLayout; m_btn_layout->addSpacing(192); m_btn_cancel->setText(tr("cancel")); m_btn_ok->setText(tr("ok")); m_btn_layout->addWidget(m_btn_cancel); m_btn_layout->addWidget(m_btn_ok); m_main_layout->addSpacing(20); m_btn_ok->setAutoDefault(true); m_btn_cancel->setAutoDefault(false); m_main_layout->addLayout(m_btn_layout); setLayout(m_main_layout); m_usr_btn_usr->setChecked(true); m_reg_usr_combox->setChecked (false); QMap uriList = GlobalSettings::getInstance()->getValue(REMOTE_SERVER_REMOTE_IP).toMap(); QString portStr = QString::number(url.port()); QString type = url.scheme(); if (portStr.toInt() < 0) { if (ftpTypeStr == type.toLower()) { portStr = ftpDefaultPortStr; } else if (sftpTypeStr == type.toLower()) { portStr = sftpDefaultPortStr; } else if ("smb"==type.toLower()) {/* samba服务是smb */ portStr = sambaDefaultPortStr; } } QString remoteUri= type.append("://").append(url.host()).append(":").append(portStr); if (uriList.contains (remoteUri)) { QMap userInfo = uriList[remoteUri].toMap (); if (!userInfo.isEmpty ()) { m_userInfo = userInfo; for (auto u : userInfo.keys ()) { m_reg_usr_name_editor->addItem (u); } // 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_combox->setChecked(true); } } } 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 ())); m_reg_usr_combox->setChecked(true); } }); connect(m_usr_btn_guest, &QRadioButton::clicked, [=] () { setFixedSize(m_widget_size_little); m_reg_usr_combox->setHidden(true); m_reg_usr_name_label->setHidden(true); m_reg_usr_name_editor->setHidden(true); m_reg_usr_passwd_label->setHidden(true); m_reg_usr_passwd_editor->setHidden(true); }); connect(m_usr_btn_usr, &QRadioButton::clicked, [=] () { setFixedSize(m_widget_size); m_reg_usr_combox->setHidden(false); m_reg_usr_name_label->setHidden(false); m_reg_usr_name_editor->setHidden(false); m_reg_usr_passwd_label->setHidden(false); m_reg_usr_passwd_editor->setHidden(false); }); connect (m_reg_usr_combox, &QCheckBox::clicked, this, [=] (bool checked) { if (!checked) { // FIXME:// 是否清楚已保存的用户名和密码? } }); connect(m_btn_cancel, &QPushButton::clicked, [=] () { close(); }); connect(m_btn_ok, &QPushButton::clicked, [=] () { accept(); QUrl url(m_remoteIP); syncRemoteServer(url); }); } ConnectServerLogin::~ConnectServerLogin() { } QString ConnectServerLogin::user() { return m_reg_usr_name_editor->currentText (); } QString ConnectServerLogin::domain() { return "WORKGROUP"; } QString ConnectServerLogin::password() { return m_reg_usr_passwd_editor->text(); } bool ConnectServerLogin::anonymous() { return m_usr_btn_guest->isChecked() ? true : false; } bool ConnectServerLogin::savePassword() { return m_reg_usr_combox->isChecked(); } void ConnectServerLogin::syncRemoteServer(const QUrl& url) { if (GlobalSettings::getInstance()->isExist(REMOTE_SERVER_REMOTE_IP)) { QMap uriList = GlobalSettings::getInstance()->getValue(REMOTE_SERVER_REMOTE_IP).toMap(); QString portStr = QString::number(url.port()); QString type = url.scheme(); if(portStr.toInt()< 0) { if(ftpTypeStr==type.toLower()){ portStr = ftpDefaultPortStr; }else if(sftpTypeStr==type.toLower()){ portStr = sftpDefaultPortStr; }else if("smb"==type.toLower()){/* samba服务是smb */ portStr = sambaDefaultPortStr; } } QString remoteUri= type.append("://").append(url.host()).append(":").append(portStr); QMap userInfo; if (!uriList.contains (remoteUri)) { if (savePassword () && !m_reg_usr_passwd_editor->text().isEmpty ()) { userInfo.insert (m_reg_usr_name_editor->currentText (), passwdEncode (m_reg_usr_passwd_editor->text().toUtf8 ())); } uriList.insert (remoteUri, userInfo); GlobalSettings::getInstance()->slot_updateRemoteServer(remoteUri, true); } else { userInfo = uriList[remoteUri].toMap (); if (savePassword () && !m_reg_usr_passwd_editor->text().isEmpty ()) { userInfo[m_reg_usr_name_editor->currentText ()] = passwdEncode (m_reg_usr_passwd_editor->text().toUtf8 ()); } /*else { if (userInfo.contains (m_reg_usr_name_editor->currentText ())) { userInfo.remove (m_reg_usr_name_editor->currentText ()); } }*/ uriList[remoteUri] = userInfo; } GlobalSettings::getInstance()->setValue(REMOTE_SERVER_REMOTE_IP,uriList); GlobalSettings::getInstance()->forceSync(REMOTE_SERVER_REMOTE_IP); } } static const unsigned char PEONY_AES_KEY[] = "peony key"; static QString passwdEncode (QString p) { g_return_val_if_fail (!p.isEmpty (), ""); unsigned char aesKey[AES_BLOCK_SIZE] = {0}; unsigned char ivc[AES_BLOCK_SIZE] = {0}; int passwdLength = p.toUtf8().size () + 1; if (passwdLength % AES_BLOCK_SIZE) passwdLength += (passwdLength % AES_BLOCK_SIZE); g_autofree unsigned char* tmp = (unsigned char*) g_malloc0 (passwdLength); memcpy (tmp, p.toUtf8 ().constData (), p.toUtf8 ().size ()); int encslength = ((passwdLength + AES_BLOCK_SIZE) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE; g_autofree unsigned char* encodeOut = (unsigned char*) g_malloc0 (encslength + 1); memset (ivc, 0, sizeof (ivc)); AES_KEY encKey; memcpy (aesKey, PEONY_AES_KEY, sizeof (PEONY_AES_KEY)); AES_set_encrypt_key (aesKey, 128, &encKey); AES_cbc_encrypt (tmp, encodeOut, p.toUtf8 ().size (), &encKey, ivc, AES_ENCRYPT); return QString("%1|%2").arg (p.toUtf8 ().size ()).arg (QString (QByteArray::fromRawData ((char*) encodeOut, encslength).toBase64 ())); } static QString passwdDecode (QString p) { g_return_val_if_fail (!p.isEmpty (), ""); QStringList ls = p.split ("|"); g_return_val_if_fail (ls.length () == 2, ""); unsigned char aesKey[AES_BLOCK_SIZE] = {0}; unsigned char ivc[AES_BLOCK_SIZE] = {0}; int srcPasswdLength = ls.first ().toInt (); QByteArray data = QByteArray::fromBase64 (ls.last ().toUtf8 ()); int encodePasswdLength = data.length (); if (encodePasswdLength % AES_BLOCK_SIZE) encodePasswdLength += (encodePasswdLength % AES_BLOCK_SIZE); g_autofree unsigned char* tmp = (unsigned char*) g_malloc0 (encodePasswdLength); memcpy (tmp, data.constData (), data.length ()); g_autofree unsigned char* decodeOut = (unsigned char*) g_malloc0 (srcPasswdLength + 1); memset (ivc, 0, sizeof (ivc)); memcpy (aesKey, PEONY_AES_KEY, sizeof (PEONY_AES_KEY)); AES_KEY decKey; AES_set_decrypt_key (aesKey, 128, &decKey); AES_cbc_encrypt (tmp, decodeOut, srcPasswdLength, &decKey, ivc, AES_DECRYPT); return (char*) decodeOut; } peony/libpeony-qt/linux-pwd-helper.cpp0000644000175000017500000000267214205115226017010 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "linux-pwd-helper.h" #include LinuxPWDHelper::LinuxPWDHelper() { } const QList LinuxPWDHelper::getAllUserInfos() { setpwent(); QList l; struct passwd *user; while((user = getpwent())!=nullptr) { l<pw_name; m_full_name = user->pw_gecos; m_home_dir = user->pw_dir; m_shell_dir = user->pw_shell; m_uid = user->pw_uid; m_gid = user->pw_gid; } const PWDItem LinuxPWDHelper::getCurrentUser() { uid_t uid = geteuid(); struct passwd *pw = getpwuid(uid); return PWDItem(pw); } peony/libpeony-qt/data/0000755000175000017500000000000014205115226014002 5ustar fengfengpeony/libpeony-qt/data/libpeony-qt-styled.qss0000644000175000017500000000077714205101223020302 0ustar fengfengPeony--DirectoryView--IconView::Item { padding-top: 5px; padding-bottom: 10px; image-position: bottom; } Peony--DirectoryView--IconView::Item::hover { background-color: transparent; } Peony--DirectoryView--IconView::Item::selected { background-color: transparent; } Peony--DirectoryView--ListView::Item::hover { background-color: transparent; } Peony--SideBar::Item::hover { background-color: transparent; } Peony--SideBar::Item::selected { background-color: transparent; } peony/libpeony-qt/data/libpeony-qt-light.qss0000644000175000017500000000126114205115226020102 0ustar fengfengPeony--DirectoryView--IconView { margin: 0px; border: 0px; background-color: white; font: 16px; color: black; } Peony--DirectoryView--IconView::Item { padding-top: 10px; padding-bottom: 10px; image-position: bottom; } Peony--DirectoryView--IconView::Item::hover { background: #cfe6fd; color: black; } Peony--DirectoryView--IconView::Item::selected{ background: #bfd6f7; color: black; } Peony--SideBar { border: 0; padding: 0; background-color: white; color: black; } Peony--SideBar::Item::hover { background: #cfe6fd; color: black; } Peony--SideBar::Item::selected{ background: #bfd6f7; color: black; } peony/libpeony-qt/data/libpeony-qt-dark.qss0000644000175000017500000000206214205101223017704 0ustar fengfengPeony--DirectoryView--IconView { margin: 0px; border: 0px; background-color: #283138; font: 16px; color: #cccccc; } Peony--DirectoryView--IconView::Item { padding-top: 10px; padding-bottom: 10px; image-position: bottom; } Peony--DirectoryView--IconView::Item::hover { background: #697883; color: #cccccc; } Peony--DirectoryView--IconView::Item::selected{ background: #8897a3; color: #cccccc; } Peony--SideBar { show-decoration-selected: 1; border: 0px; padding: 0px; margin: 0px; background-color: #283138; font: 16px; color: #cccccc; } Peony--SideBar::Item { show-decoration-selected: 1; } Peony--SideBar::Branch { background: transparent; color: #cccccc; } Peony--SideBar::Branch::hover { background: #697883; color: #cccccc; } Peony--SideBar::Branch::selected { background: #8897a3; color: #cccccc; } Peony--SideBar::Item::hover { background: #697883; color: #cccccc; } Peony--SideBar::Item::selected{ background: #8897a3; color: #cccccc; } peony/libpeony-qt/development-files/0000755000175000017500000000000014205101223016503 5ustar fengfengpeony/libpeony-qt/development-files/header-files/0000755000175000017500000000000014205101223021033 5ustar fengfengpeony/libpeony-qt/development-files/header-files/PeonyFileWatcher0000644000175000017500000000003214205101223024161 0ustar fengfeng#include peony/libpeony-qt/development-files/header-files/PeonyFileInfoManager0000644000175000017500000000003714205101223024757 0ustar fengfeng#include "file-info-manager.h" peony/libpeony-qt/development-files/header-files/PeonyFileUtils0000644000175000017500000000002614205101223023667 0ustar fengfeng#include "file-utils" peony/libpeony-qt/development-files/header-files/PeonySearchVFSRegister0000644000175000017500000000004114205101223025255 0ustar fengfeng#include "search-vfs-register.h" peony/libpeony-qt/development-files/header-files/PeonyFileInfoJob0000644000175000017500000000003314205101223024113 0ustar fengfeng#include peony/libpeony-qt/development-files/header-files/PeonyQtAPI0000644000175000017500000000031514205101223022706 0ustar fengfeng#include #include #include #include #include #include #include peony/libpeony-qt/development-files/header-files/model/0000755000175000017500000000000014205115226022143 5ustar fengfengpeony/libpeony-qt/development-files/header-files/model/PeonyFileItemProxyModel0000644000175000017500000000005714205101223026614 0ustar fengfeng#include peony/libpeony-qt/development-files/header-files/model/PeonyFileItemModel0000644000175000017500000000003514205101223025546 0ustar fengfeng#include peony/libpeony-qt/development-files/header-files/model/PeonySideBarProxyModel0000644000175000017500000000005614205101223026426 0ustar fengfeng#include peony/libpeony-qt/development-files/header-files/model/PeonyFileItem0000644000175000017500000000002714205101223024566 0ustar fengfeng#include peony/libpeony-qt/development-files/header-files/model/PeonyPathBarModel0000644000175000017500000000003414205101223025370 0ustar fengfeng#include peony/libpeony-qt/development-files/header-files/model/PeonySideBarModel0000644000175000017500000000025714205115226025377 0ustar fengfeng#include #include #include #include #include peony/libpeony-qt/development-files/header-files/model/PeonyPathCompleter0000644000175000017500000000003414205101223025635 0ustar fengfeng#include peony/libpeony-qt/development-files/header-files/PeonyFileEnumerator0000644000175000017500000000003514205101223024710 0ustar fengfeng#include peony/libpeony-qt/development-files/header-files/fileop/0000755000175000017500000000000014205101223022311 5ustar fengfengpeony/libpeony-qt/development-files/header-files/fileop/PeonyFileOperationErrorHandler0000644000175000017500000000012314205101223030313 0ustar fengfeng#include #include peony/libpeony-qt/development-files/header-files/fileop/PeonyFileOperationManager0000644000175000017500000000004414205101223027300 0ustar fengfeng#include peony/libpeony-qt/development-files/header-files/fileop/PeonyFileOperationWizard0000644000175000017500000000005414205101223027167 0ustar fengfeng#include peony/libpeony-qt/development-files/header-files/fileop/PeonyFileOperations0000644000175000017500000000047614205101223026201 0ustar fengfeng#include #include #include #include #include #include #include #include #include #include peony/libpeony-qt/development-files/header-files/PeonyVolumeManager0000644000175000017500000000003414205101223024530 0ustar fengfeng#include peony/libpeony-qt/development-files/header-files/PeonySearchVFSManager0000644000175000017500000000004014205101223025042 0ustar fengfeng#include "search-vfs-manager.h" peony/libpeony-qt/development-files/header-files/PeonyMountOperation0000644000175000017500000000003514205101223024752 0ustar fengfeng#include peony/libpeony-qt/development-files/header-files/PeonyFileInfo0000644000175000017500000000002714205101223023463 0ustar fengfeng#include peony/libpeony-qt/development-files/header-files/PeonyGObjectWrapper0000644000175000017500000000007214205101223024646 0ustar fengfeng#include #include peony/libpeony-qt/gobject-template.h0000644000175000017500000000733514205115226016500 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef GOBJECTTEMPLATE_H #define GOBJECTTEMPLATE_H #include "peony-core_global.h" #include #include #include #include namespace Peony { template /*! * \brief The GObjectTemplate class *
* I have to say that managing the GObject data memory manually is harder than i thought. * This template class provide a way to wrap the GObject handle to a smart pointer. * and manage them automaticly. * for example, if you have a GFile handle and you really don't know when the handle will * be deleted. you can use 'auto filewrapper = wrapGFile(file)', then you should hold the * left value, and use 'filewrapper.get()' to get the GFile handle if you need. *
*/ class PEONYCORESHARED_EXPORT GObjectTemplate { public: //do not use this constructor. GObjectTemplate(); GObjectTemplate(T *obj, bool ref = false) { m_obj = obj; if (ref) { g_object_ref(obj); } } ~GObjectTemplate() { //qDebug()<<"~GObjectTemplate"; if (m_obj) g_object_unref(m_obj); } T *get() { return m_obj; } private: mutable T *m_obj = nullptr; }; //typedef typedef std::shared_ptr> GFileWrapperPtr; typedef std::shared_ptr> GFileInfoWrapperPtr; typedef std::shared_ptr> GFileEnumeratorWrapperPtr; typedef std::shared_ptr> GFileMonitorWrapperPtr; typedef std::shared_ptr> GVolumeMonitorWrapperPtr; typedef std::shared_ptr> GDriveWrapperPtr; typedef std::shared_ptr> GVolumeWrapperPtr; typedef std::shared_ptr> GMountWrapperPtr; typedef std::shared_ptr> GIconWrapperPtr; typedef std::shared_ptr> GThemedIconWrapperPtr; typedef std::shared_ptr> GCancellableWrapperPtr; std::shared_ptr> wrapGFile(GFile *file); std::shared_ptr> wrapGFileInfo(GFileInfo *info); std::shared_ptr> wrapGFileEnumerator(GFileEnumerator *enumerator); std::shared_ptr> wrapGFileMonitor(GFileMonitor *monitor); std::shared_ptr> wrapGVolumeMonitor(GVolumeMonitor *monitor); std::shared_ptr> wrapGDrive(GDrive *drive); std::shared_ptr> wrapGVolume(GVolume *volume); std::shared_ptr> wrapGMount(GMount *mount); std::shared_ptr> wrapGIcon(GIcon *icon); std::shared_ptr> wrapGThemedIcon(GThemedIcon *icon); std::shared_ptr> wrapGCancellable(GCancellable *cancellable); /* std::shared_ptr> wrap() { return std::make_shared>(); } */ } #endif // GOBJECTTEMPLATE_H peony/libpeony-qt/connect-server-dialog.h0000644000175000017500000000235514205101223017431 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef CONNECTSERVERDIALOG_H #define CONNECTSERVERDIALOG_H #include namespace Ui { class ConnectServerDialog; } class ConnectServerDialog : public QDialog { Q_OBJECT public: explicit ConnectServerDialog(QWidget *parent = nullptr); ~ConnectServerDialog(); QString user(); QString password(); QString domain(); bool savePassword(); bool anonymous(); private: Ui::ConnectServerDialog *ui; }; #endif // CONNECTSERVERDIALOG_H peony/libpeony-qt/gerror-wrapper.h0000644000175000017500000000263114205101223016212 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef GIOERROR_H #define GIOERROR_H #include "peony-core_global.h" #include #include #include namespace Peony { class PEONYCORESHARED_EXPORT GErrorWrapper { public: GErrorWrapper();//do not use this constructor. GErrorWrapper(GError *err); ~GErrorWrapper(); int code(); QString message(); QString domain(); static std::shared_ptr wrapFrom(GError *err); private: GError *m_err = nullptr; }; typedef std::shared_ptr GErrorWrapperPtr; } Q_DECLARE_METATYPE(Peony::GErrorWrapper) Q_DECLARE_METATYPE(Peony::GErrorWrapperPtr) #endif // GIOERROR_H peony/libpeony-qt/custom-error-handler.cpp0000644000175000017500000000251614205101223017645 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2021, 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 . * * Authors: Yue Lan * */ #include "custom-error-handler.h" #include using namespace Peony; CustomErrorHandler::CustomErrorHandler(QObject *parent) : QObject(parent) { } QList CustomErrorHandler::errorCodeSupportHandling() { return QList(); } void CustomErrorHandler::handleCustomError(const QString &uri, int errorCode) { QMessageBox::StandardButton button = QMessageBox::question(0, 0, tr("Is Error Handled?")); if (button == QMessageBox::Yes) { Q_EMIT finished(); } else { Q_EMIT failed(tr("Error not be handled correctly")); } } peony/libpeony-qt/gobject-template.cpp0000644000175000017500000000501414205101223017013 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "gobject-template.h" namespace Peony { std::shared_ptr> wrapGFile(GFile *file) { return std::make_shared>(file); } std::shared_ptr> wrapGFileInfo(GFileInfo *info) { return std::make_shared>(info); } std::shared_ptr> wrapGFileEnumerator(GFileEnumerator *enumerator) { return std::make_shared>(enumerator); } std::shared_ptr> wrapGFileMonitor(GFileMonitor *monitor) { return std::make_shared>(monitor); } std::shared_ptr> wrapGVolumeMonitor(GVolumeMonitor *monitor) { return std::make_shared>(monitor); } std::shared_ptr> wrapGDrive(GDrive *drive) { return std::make_shared>(drive); } std::shared_ptr> wrapGVolume(GVolume *volume) { return std::make_shared>(volume); } std::shared_ptr> wrapGMount(GMount *mount) { return std::make_shared>(mount); } std::shared_ptr> wrapGIcon(GIcon *icon) { return std::make_shared>(icon); } std::shared_ptr> wrapGThemedIcon(GThemedIcon *icon) { return std::make_shared>(icon); } std::shared_ptr> wrapGCancellable(GCancellable *cancellable) { return std::make_shared>(cancellable); } /* std::shared_ptr> wrap() { return std::make_shared>(); } */ } peony/libpeony-qt/file-info.cpp0000644000175000017500000001744614205115226015461 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include #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 #include #include using namespace Peony; FileInfo::FileInfo(QObject *parent) : QObject (parent) { m_cancellable = g_cancellable_new(); } FileInfo::FileInfo(const QString &uri, QObject *parent) : QObject (parent) { m_cancellable = g_cancellable_new(); /*! * \note * In qt program we alwas handle file's uri format as unicode, * but in glib/gio it might be not. * I want to keep the uri strings format in peony-qt, * this would help me avoid some problem, such as the uri path completion * bug in PathBarModel enumeration. */ m_uri = uri; m_file = g_file_new_for_uri(uri.toUtf8().data()); m_parent = g_file_get_parent(m_file); m_is_remote = !g_file_is_native(m_file); GFileType type = g_file_query_file_type(m_file, G_FILE_QUERY_INFO_NONE, nullptr); switch (type) { case G_FILE_TYPE_DIRECTORY: //qDebug()<<"dir"; m_is_dir = true; break; case G_FILE_TYPE_MOUNTABLE: //qDebug()<<"mountable"; m_is_volume = true; break; default: break; } } FileInfo::~FileInfo() { ThumbnailManager::getInstance()->releaseThumbnail(m_uri); //qDebug()<<"~FileInfo"< FileInfo::fromUri(QString uri) { FileInfoManager *info_manager = FileInfoManager::getInstance(); info_manager->lock(); std::shared_ptr info = info_manager->findFileInfoByUri(uri); if (info != nullptr) { info_manager->unlock(); return info; } else { std::shared_ptr newly_info = std::make_shared(); newly_info->m_uri = uri; newly_info->m_file = g_file_new_for_uri(uri.toUtf8().data()); newly_info->m_parent = g_file_get_parent(newly_info->m_file); newly_info->m_is_remote = ! g_file_is_native(newly_info->m_file); if (! newly_info->m_is_remote && false) { GFileType type = g_file_query_file_type(newly_info->m_file, G_FILE_QUERY_INFO_NONE, nullptr); switch (type) { case G_FILE_TYPE_DIRECTORY: //qDebug()<<"dir"; newly_info->m_is_dir = true; break; case G_FILE_TYPE_MOUNTABLE: //qDebug()<<"mountable"; newly_info->m_is_volume = true; break; default: break; } } newly_info = info_manager->insertFileInfo(newly_info); info_manager->unlock(); return newly_info; } } std::shared_ptr FileInfo::fromPath(QString path) { QString uri = "file://" + path; return fromUri(uri); } std::shared_ptr FileInfo::fromGFile(GFile *file) { char *uri_str = g_file_get_uri(file); QString uri = uri_str; g_free(uri_str); return fromUri(uri); } /******* 函数功能:判断文件是否是视频文件 一般的视频文件都是 video/*,但是有些视频文件比较特殊 比如: asf : application/vnd.ms-asf rmvb: application/vnd.rn-realmedia swf : application/vnd.adobe.flash.movie ts : text/vnd.trolltech.linguist h264: application/octet-stream 目前上面这些是已经测试到的视频文件类型,如果有没有覆盖到的情况后面再补充 **/ bool FileInfo::isVideoFile() { if (nullptr != m_mime_type_string) { if (m_mime_type_string.startsWith("video") || m_mime_type_string.endsWith("vnd.trolltech.linguist") || m_mime_type_string.endsWith("vnd.adobe.flash.movie") || m_mime_type_string.endsWith("vnd.rn-realmedia") || m_mime_type_string.endsWith("vnd.ms-asf") || m_mime_type_string.endsWith("octet-stream")) { return true; } else { return false; } } else { return false; } } bool FileInfo::isOfficeFile() { int idx = 0; QString mtype = nullptr; for (idx = 0; office_mime_types[idx] != "end"; idx++) { mtype = office_mime_types[idx]; if (m_mime_type_string.contains(mtype)) { return true; } } return false; } const QString FileInfo::targetUri() { return m_target_uri; } const QString FileInfo::symlinkTarget() { if (m_symlink_target == ".") return ""; //fix soft link use relative path issue, link to bug#73529 if (! m_symlink_target.startsWith("/")) { QString parentUri = FileUtils::getParentUri(m_uri); m_symlink_target = QUrl(parentUri).path() + "/" + m_symlink_target; } return m_symlink_target; } const QString FileInfo::customIcon() { if (!m_meta_info) return nullptr; return m_meta_info.get()->getMetaInfoString("custom-icon"); } quint64 FileInfo::getDeletionDateUInt64() { return m_deletion_date_uint64; } const QString FileInfo::unixDeviceFile() { GFile* file; const char *path; bool isMountPoint; GUnixMountEntry* entry; char* device = nullptr; QString unixDevice = nullptr; isMountPoint = FileUtils::isMountPoint(m_uri); //return from here if @m_uri is like "computer:///xxx" if(!isMountPoint) return m_unix_device_file; //query device path if @m_uri is like "file:///media/user/xxx" file = g_file_new_for_uri(m_uri.toUtf8().constData()); if(!file) return unixDevice; path = g_file_peek_path(file); if(path){ entry = g_unix_mount_at(path,NULL); if(!entry) entry = g_unix_mount_for(path,NULL); if(entry){ device = g_strescape(g_unix_mount_get_device_path(entry),NULL); g_unix_mount_free(entry); } } unixDevice = device; g_object_unref(file); if(device) g_free(device); return unixDevice; } const QString FileInfo::displayName() { if (isEmptyInfo()) return nullptr; bool isMountPoint; QString unixDevice,deviceName; unixDevice = unixDeviceFile(); isMountPoint = FileUtils::isMountPoint(m_uri); if(m_uri == "file:///DATA") { return tr("data"); } if((nullptr != m_display_name) && (!isMountPoint || unixDevice.isEmpty() /*@m_uri is like "computer:///xxx"*/ || !unixDevice.contains("/dev") /*audio-cd*/ || unixDevice.contains("/dev/sr"))) { /*blank-cd or blank-dvd*/ return m_display_name; } if (m_uri.endsWith("/")) { QString uri = m_uri; if (!m_uri.endsWith(":///") && !m_uri.endsWith("://")) { uri.chop(1); } return uri.split("/").last(); } //@deviceName transcoding deviceName = m_display_name; FileUtils::handleVolumeLabelForFat32(deviceName,unixDevice); return deviceName; } peony/libpeony-qt/test/0000755000175000017500000000000014205101223014040 5ustar fengfengpeony/libpeony-qt/test/test.pro0000644000175000017500000000212014205101223015534 0ustar fengfeng#------------------------------------------------- # # Project created by QtCreator 2019-07-24T10:27:31 # #------------------------------------------------- TARGET = test TEMPLATE = app QT += core widgets gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = peony-qt-core-test TEMPLATE = app CONFIG += link_pkgconfig no_keywords c++11 PKGCONFIG += glib-2.0 gio-2.0 # The following define makes your compiler emit warnings if you use # any feature of Qt which has been marked as 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 you use 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 include(../peony-core.pri) SOURCES += testwidget.cpp main.cpp HEADERS += testwidget.h peony/libpeony-qt/test/testwidget.cpp0000644000175000017500000001457614205101223016744 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #include "testwidget.h" #include "file-info.h" #include "file-info-job.h" #include "file-info-manager.h" #include "file-enumerator.h" #include "mount-operation.h" #include "file-watcher.h" #include "volume-manager.h" #include "gerror-wrapper.h" #include TestWidget::TestWidget(QWidget *parent) : QWidget(parent) { auto info = Peony::FileInfo::fromUri("file:///"); qDebug()<uri(); //get uri of info. QString uri = info->uri(); connect(info.get(), &Peony::FileInfo::updated, [=]() { qDebug()<<"this info was updated"; //qDebug()<iconName(); //this is uncorrect. be caleful that using info shared_ptr in lambda also causes ref count increased. auto file_info = Peony::FileInfoManager::getInstance()->findFileInfoByUri(uri); //this is correct. qDebug()<iconName(); }); Peony::FileInfoJob *job = new Peony::FileInfoJob(info, nullptr); job->setAutoDelete(true); job->querySync(); qDebug()<iconName(); for (int i = 0; i < 4; i++) { Peony::FileInfoJob *job2 = new Peony::FileInfoJob(info, nullptr); job2->setAutoDelete(true); job2->connect(job2, &Peony::FileInfoJob::queryAsyncFinished, [=](bool successed) { qDebug()<queryAsync(); //job2->cancel();//if we canceled the job, file info will not be modified send updated signal. qDebug()<<"queryAsync"; } info.reset(); //do not use shared_ptr with lambda. //auto enumerator = std::make_shared(); auto enumerator = new Peony::FileEnumerator; //enumerator->setEnumerateDirectory("computer:///KINGSTON%20RBU-SNS8152S3256GG5.drive"); //enumerator->setEnumerateDirectory("sftp://112.124.201.32"); //enumerator->setEnumerateDirectory("smb://lacie-5big.local/share/"); //enumerator->setEnumerateDirectory("network:///"); //enumerator->setEnumerateDirectory("network:///dnssd-domain-MC._smb._tcp"); //enumerator->setEnumerateDirectory("network:///dnssd-domain-LaCie-5big._smb._tcp"); //enumerator->setEnumerateDirectory("file:///home/lanyue"); //enumerator->setEnumerateDirectory("file:///root"); enumerator->setEnumerateDirectory("file:///"); enumerator->prepare(); connect(enumerator/*.get()*/, &Peony::FileEnumerator::prepared, [=](std::shared_ptr prepared_err) { qDebug()<<"prepared"; if (prepared_err) { qDebug()<code()<message(); } //sync enumerate /* enumerator->enumerateSync(); auto list = enumerator->getChildren(); for (auto child : list) { qDebug()<uri()<disconnect(); delete enumerator; */ //async enumerate this->connect(enumerator, &Peony::FileEnumerator::childrenUpdated, [=](const QStringList &uriList) { if (uriList.isEmpty()) return; qDebug()<<"update async:"<connect(enumerator, &Peony::FileEnumerator::enumerateFinished, [=](bool successed) { if (!successed) { qDebug()<<"failed enumerate children"; return; } auto list = enumerator->getChildren(); for (auto child : list) { qDebug()<uri()<disconnect(); delete enumerator; }); enumerator->enumerateAsync(); }); //Peony::FileWatcher *watcher = new Peony::FileWatcher("file:///home/lanyue/gvfs-test"); Peony::FileWatcher *watcher = new Peony::FileWatcher("computer:///"); connect(watcher, &Peony::FileWatcher::locationChanged, [=](QString old, QString newly) { qDebug()<<"monitor location changed"<startMonitor(); Peony::VolumeManager *volumeManager = Peony::VolumeManager::getInstance(); connect(volumeManager, &Peony::VolumeManager::driveConnected, [=](std::shared_ptr drive) { auto name = drive->name(); qDebug()<<"drive conneceted"< drive) { auto name = drive->name(); qDebug()<<"drive disconneceted"< volume) { auto name = volume->name(); qDebug()<<"volume added"< volume) { auto name = volume->name(); qDebug()<<"volume removed"< mount) { auto name = mount->name(); qDebug()<<"mount added"< mount) { auto name = mount->name(); qDebug()<<"mount removed"<. * * Authors: Yue Lan * */ #ifndef TESTWIDGET_H #define TESTWIDGET_H #include class TestWidget : public QWidget { Q_OBJECT public: explicit TestWidget(QWidget *parent = nullptr); }; #endif // TESTWIDGET_H peony/libpeony-qt/test/main.cpp0000644000175000017500000000175714205101223015502 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #include "testwidget.h" #include #include int main(int argc, char *argv[]) { QApplication a(argc, argv); TestWidget w; w.show(); return a.exec(); } peony/libpeony-qt/file-meta-info.h0000644000175000017500000000511114205101223016024 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILEMETAINFO_H #define FILEMETAINFO_H #include #include #include #include #include #include #include #include namespace Peony { /*! * \brief The FileMetaInfo class * \details * This class represent a data set abstracted from gvfs metadata. * * When a FileInfoJob queryed, a FileMetaInfo will be created for the FileInfo instance * which FileInfoJob hold. FileMetaInfo constructed with the GFileInfo handle the file query * job executed, and read all "metadata::" namespace datas then put them into a hash table. * * \note * You can use FileInfoMeta::fromUri(uri) to get a file's meta data in global, but you should make * sure that file's FileInfo is queryed yet. */ class PEONYCORESHARED_EXPORT FileMetaInfo { friend class FileInfo; friend class FileInfoJob; friend class FileInfoManager; public: static std::shared_ptr fromGFileInfo(const QString &uri, GFileInfo *g_info); static std::shared_ptr fromUri(const QString &uri); FileMetaInfo(const QString &uri, GFileInfo *g_info); void setMetaInfoString(const QString &key, const QString &value); const QString getMetaInfoString(const QString &key); void setMetaInfoStringList(const QString &key, const QStringList &value); const QStringList getMetaInfoStringList(const QString &key); void setMetaInfoInt(const QString &key, int value); int getMetaInfoInt(const QString &key); void setMetaInfoVariant(const QString &key, const QVariant &value, bool syncToFile = true); const QVariant getMetaInfoVariant(const QString &key); void removeMetaInfo(const QString &key); private: QString m_uri; QHash m_meta_hash; QMutex m_mutex; }; } #endif // FILEMETAINFO_H peony/libpeony-qt/file-operation/0000755000175000017500000000000014205115226016006 5ustar fengfengpeony/libpeony-qt/file-operation/file-operation-error-dialogs.cpp0000644000175000017500000003475614205115226024215 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Jing Ding * */ #include "file-operation-error-dialogs.h" #include #include #include #include #include #include #include #include #include static QPixmap drawSymbolicColoredPixmap (const QPixmap& source); Peony::FileOperationErrorDialogConflict::FileOperationErrorDialogConflict(FileOperationErrorDialogBase *parent) : FileOperationErrorDialogBase(parent) { setFixedSize(m_fix_width, m_fix_height); setContentsMargins(9, 9, 9, 9); // file icon m_file_icon = new QLabel(this); m_file_icon->setGeometry(m_file_x, m_file_y, m_file_size, m_file_size); m_file_icon->setPixmap(QIcon::fromTheme(m_file_icon_name.isEmpty()?"text-x-plain":m_file_icon_name).pixmap(QSize(m_file_size, m_file_size))); m_tip = new QLabel(this); m_tip->setTextFormat(Qt::RichText); m_tip->setBackgroundRole(QPalette::Link); m_tip->setText(QString("

%1 %3

") .arg(tr("This location already contains the file,")) .arg(tr("Do you want to override it?"))); m_tip->setWordWrap(true); m_tip->setGeometry(m_tip_x, m_tip_y, m_tip_width, m_tip_height); // replace m_rp_btn = new QPushButton(this); m_rp_btn->setText(tr("Replace")); m_rp_btn->setBackgroundRole(QPalette::Button); m_rp_btn->setGeometry(m_rp_btn_x, m_rp_btn_y, m_rp_btn_width, m_rp_btn_height); // ignore m_ig_btn = new QPushButton(this); m_ig_btn->setText(tr("Ignore")); m_ig_btn->setGeometry(m_ig_btn_x, m_ig_btn_y, m_ig_btn_width, m_ig_btn_height); // backup m_bk_btn = new QPushButton(this); m_bk_btn->setText(tr("Backup")); m_bk_btn->setGeometry(m_bk_btn_x, m_bk_btn_y, m_bk_btn_width, m_bk_btn_height); // Then do the same thing m_sm_ck = new QCheckBox(this); m_sm_ck->setText(tr("Do the same")); m_sm_ck->setGeometry(m_sm_btn_x, m_sm_btn_y, m_sm_btn_width / 2, m_sm_btn_height); connect(m_sm_ck, &QCheckBox::stateChanged, this, [=](int chose) { switch (chose) { case Qt::Checked: m_do_same = true; break; case Qt::Unchecked: default: m_do_same = false; } }); connect(m_rp_btn, &QPushButton::pressed, this, [=] () { m_ignore = false; m_backup = false; m_replace = true; done(QDialog::Accepted); }); connect(m_ig_btn, &QPushButton::pressed, this, [=] () { m_ignore = true; m_backup = false; m_replace = false; done(QDialog::Accepted); }); connect(m_bk_btn, &QPushButton::pressed, this, [=] () { m_ignore = false; m_backup = true; m_replace = false; done(QDialog::Accepted); }); } Peony::FileOperationErrorDialogConflict::~FileOperationErrorDialogConflict() { } void Peony::FileOperationErrorDialogConflict::setTipFilename(QString name) { if (!name.isEmpty()) { QStyleOptionViewItem opt; m_file_name = QUrl(name).toDisplayString(); m_tip->setText(QString(tr("

This location already contains the file '%1', Do you want to override it?

")) .arg(opt.fontMetrics.elidedText(m_file_name, Qt::ElideMiddle, 480))); } } void Peony::FileOperationErrorDialogConflict::setTipFileicon(QString icon) { if (!icon.isEmpty()) { m_file_icon_name = icon; m_file_icon->setPixmap(QIcon::fromTheme(m_file_icon_name.isEmpty()?"text-x-plain":m_file_icon_name).pixmap(QSize(m_file_size, m_file_size))); } } void Peony::FileOperationErrorDialogConflict::handle (FileOperationError& error) { m_error = &error; if (FileOpRename == m_error->op) { FileInfoJob file(error.destDirUri, nullptr); file.querySync(); setTipFileicon(file.getInfo()->iconName()); setTipFilename(file.getInfo()->displayName()); } else if (FileOpUntrash == m_error->op) { // The Recycle Bin has special treatment for files with the same name QString srcFileName = error.srcUri.split("/").back(); QString srcFileNoExt = srcFileName.split(".").first(); QString destFileName = error.destDirUri.split("/").back(); QString destFileNoExt = destFileName.split(".").first(); FileInfoJob file(error.srcUri, nullptr); file.querySync(); setTipFileicon(file.getInfo()->iconName()); if (srcFileNoExt == destFileNoExt && srcFileName.contains(destFileNoExt)) { setTipFilename(destFileName); } else { setTipFilename(file.getInfo()->displayName()); } } else { QString fileName = error.srcUri.split("/").back(); QString url = error.destDirUri.contains(fileName) ? error.destDirUri : error.destDirUri + "/" + fileName; FileInfoJob file(url, nullptr); file.querySync(); setTipFileicon(file.getInfo()->iconName()); setTipFilename(file.getInfo()->displayName()); } error.respCode = Retry; int ret = exec(); if (QDialog::Accepted == ret) { if (m_do_same) { if (m_replace) { error.respCode = OverWriteAll; } else if (m_backup) { error.respCode = BackupAll; } else if (m_ignore) { error.respCode = IgnoreAll; } else { error.respCode = Cancel; } } else { if (m_replace) { error.respCode = OverWriteOne; } else if (m_backup) { error.respCode = BackupOne; } else if (m_ignore) { error.respCode = IgnoreOne; } else { error.respCode = Cancel; } } } else { error.respCode = Cancel; } } Peony::FileOperationErrorHandler *Peony::FileOperationErrorDialogFactory::getDialog(Peony::FileOperationError &errInfo) { FileOperationErrorDialogBase* dlg = nullptr; switch (errInfo.dlgType) { case ED_CONFLICT: dlg = new FileOperationErrorDialogConflict(); dlg->setHeaderIcon("dialog-warning"); break; case ED_WARNING: dlg = new FileOperationErrorDialogWarning(); dlg->setHeaderIcon("dialog-error"); break; case ED_NOT_SUPPORTED: { dlg = new FileOperationErrorDialogNotSupported(); dlg->setHeaderIcon("dialog-information"); break; } } return dlg; } Peony::FileOperationErrorDialogWarning::FileOperationErrorDialogWarning(Peony::FileOperationErrorDialogBase *parent) : FileOperationErrorDialogBase(parent) { setFixedSize(m_fix_width, m_fix_height); setContentsMargins(9, 9, 9, 9); m_icon = new QLabel(this); m_icon->setGeometry(m_margin_lr, m_pic_top, m_pic_size, m_pic_size); m_icon->setPixmap(QIcon::fromTheme("dialog-error").pixmap(m_pic_size, m_pic_size)); m_text_scroll = new QScrollArea(this); m_text_scroll->setFrameShape(QFrame::NoFrame); m_text_scroll->setGeometry(m_margin + m_margin_lr + m_pic_size, m_text_y, width() - m_margin - m_margin_lr * 2 - m_pic_size, m_text_heigth); m_text = new QLabel(m_text_scroll); m_text->setText(""); m_text->setWordWrap(true); m_text->setMinimumWidth(width() - m_margin - m_margin_lr * 2 - m_pic_size); m_text_scroll->setWidget(m_text); m_text_scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); m_text_scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_ok = new QPushButton(this); m_ok->setText(tr("OK")); m_ok->setGeometry(m_ok_x, m_ok_y, m_ok_w, m_ok_h); m_cancel = new QPushButton(this); m_cancel->setText(tr("Cancel")); m_cancel->setGeometry(m_cancel_x, m_cancel_y, m_cancel_w, m_cancel_h); connect(m_ok, &QPushButton::pressed, this, [=](){ done (QDialog::Accepted); }); connect(m_cancel, &QPushButton::pressed, this, [=] () { done (QDialog::Rejected); }); } Peony::FileOperationErrorDialogWarning::~FileOperationErrorDialogWarning() { } void Peony::FileOperationErrorDialogWarning::handle(Peony::FileOperationError &error) { m_error = &error; QStyleOptionViewItem opt; if (nullptr != m_error->errorStr) { QString htmlString = QString("

%1

") .arg(opt.fontMetrics.elidedText(m_error->errorStr/*.toHtmlEscaped()*/, Qt::ElideMiddle, 480).toHtmlEscaped()); m_text->setText(htmlString); } else { QString htmlString = QString("

%1

") .arg(opt.fontMetrics.elidedText(tr("Make sure the disk is not full or write protected and that the file is not protected"), Qt::ElideMiddle, 480).toHtmlEscaped()); m_text->setText(htmlString); } m_text->adjustSize(); m_text->setAlignment(Qt::AlignLeft | Qt::AlignBottom); if (m_error->op && FileOpRenameToHideFile == m_error->op) { delete m_cancel; } int ret = exec(); 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; break; default: 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) { // 18, 32, 69 QPushButton m_btn; QColor baseColor = m_btn.palette().color(QPalette::Text).light(150); QImage img = source.toImage(); for (int x = 0; x < img.width(); ++x) { for (int y = 0; y < img.height(); ++y) { auto color = img.pixelColor(x, y); color.setRed(baseColor.red()); color.setGreen(baseColor.green()); color.setBlue(baseColor.blue()); img.setPixelColor(x, y, color); } } return QPixmap::fromImage(img); } Peony::FileOperationErrorDialogNotSupported::FileOperationErrorDialogNotSupported(Peony::FileOperationErrorDialogBase *parent) : FileOperationErrorDialogBase(parent) { setFixedSize(m_fix_width, m_fix_height); setContentsMargins(9, 9, 9, 9); m_icon = new QLabel(this); m_icon->setGeometry(m_margin_lr, m_pic_top, m_pic_size, m_pic_size); m_icon->setPixmap(QIcon::fromTheme("dialog-infomation").pixmap(m_pic_size, m_pic_size)); m_text_scroll = new QScrollArea(this); m_text_scroll->setFrameShape(QFrame::NoFrame); m_text_scroll->setGeometry(m_margin + m_margin_lr + m_pic_size, m_text_y, width() - m_margin - m_margin_lr * 2 - m_pic_size, m_text_heigth); m_text = new QLabel(m_text_scroll); m_text->setText(""); m_text->setWordWrap(true); m_text->setMinimumWidth(width() - m_margin - m_margin_lr * 2 - m_pic_size); m_text_scroll->setWidget(m_text); m_text_scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); m_text_scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_ok = new QPushButton(this); m_ok->setText(tr("Yes")); m_ok->setGeometry(m_ok_x, m_ok_y, m_ok_w, m_ok_h); m_cancel = new QPushButton(this); m_cancel->setText(tr("Cancel")); m_cancel->setGeometry(m_cancel_x, m_cancel_y, m_cancel_w, m_cancel_h); m_sm_ck = new QCheckBox(this); m_sm_ck->setText(tr("Do the same")); m_sm_ck->setGeometry(m_sm_btn_x, m_sm_btn_y, m_sm_btn_width, m_sm_btn_height); connect(m_ok, &QPushButton::pressed, this, [=](){ done (QDialog::Accepted); }); connect(m_cancel, &QPushButton::pressed, this, [=] () { done (QDialog::Rejected); }); } Peony::FileOperationErrorDialogNotSupported::~FileOperationErrorDialogNotSupported() { } void Peony::FileOperationErrorDialogNotSupported::handle(Peony::FileOperationError &error) { m_error = &error; QStyleOptionViewItem opt; if (nullptr != m_error->errorStr) { QString htmlString = QString("

%1

") .arg(opt.fontMetrics.elidedText(m_error->errorStr.toHtmlEscaped(), Qt::ElideMiddle, 480).toHtmlEscaped()); m_text->setText(htmlString); } else { QString htmlString = QString("

%1

") .arg(opt.fontMetrics.elidedText(tr("Make sure the disk is not full or write protected and that the file is not protected"), Qt::ElideMiddle, 480).toHtmlEscaped()); m_text->setText(htmlString); } m_text->adjustSize(); m_text->setAlignment(Qt::AlignLeft | Qt::AlignBottom); int ret = exec(); switch (m_error->errorCode) { case G_IO_ERROR_NOT_SUPPORTED: { if (!m_sm_ck->isChecked()) error.respCode = OverWriteOne; else error.respCode = OverWriteAll; break; } 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_PERMISSION_DENIED: case G_IO_ERROR_CANT_CREATE_BACKUP: case G_IO_ERROR_TOO_MANY_OPEN_FILES: error.respCode = Cancel; break; default: error.respCode = IgnoreOne; break; } if (QDialog::Accepted == ret && m_error->op == FileOpTrash && G_IO_ERROR_NOT_SUPPORTED == m_error->errorCode) { error.respCode = m_sm_ck->isChecked() ? error.respCode = ForceAll : error.respCode = Force; } else if (QDialog::Rejected == ret) { error.respCode = Cancel; } } peony/libpeony-qt/file-operation/file-rename-operation.cpp0000644000175000017500000002600014205115226022672 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-rename-operation.h" #include "file-operation-manager.h" #include "file-utils.h" #include #include #include #include #include using namespace Peony; static QString handleDuplicate(QString name) { return FileUtils::handleDuplicateName(name); } FileRenameOperation::FileRenameOperation(QString uri, QString newName) { m_uri = uri; m_new_name = FileUtils::urlDecode(newName); m_old_name = FileUtils::getFileDisplayName(uri); QStringList srcUris; srcUris<(srcUris, destUri, FileOperationInfo::Rename); } /*! * \brief FileRenameOperation::run * \bug * how to keep the permission? */ void FileRenameOperation::run() { QString destUri; Q_EMIT operationStarted(); if (m_new_name == "/" || m_new_name == "." || !nameIsValid(m_new_name)) { FileOperationError except; except.srcUri = m_uri; except.errorType = ET_GIO; except.op = FileOpRename; except.dlgType = ED_WARNING; except.title = tr("File Rename error"); except.errorStr = tr("Invalid file name %1%2%3 .").arg("\“").arg(m_new_name).arg("\”"); Q_EMIT errored(except); Q_EMIT operationFinished(); return; } else if (m_new_name.startsWith(".")) { auto showHidden = GlobalSettings::getInstance()->getValue(SHOW_HIDDEN_PREFERENCE).toBool(); if (! showHidden) { FileOperationError except; except.srcUri = m_uri; except.errorType = ET_GIO; except.op = FileOpRenameToHideFile; except.dlgType = ED_WARNING; except.title = tr("File Rename warning"); except.errorStr = tr("The file %1%2%3 will be hidden when you refresh or change directory!").arg("\“").arg(m_new_name).arg("\”"); Q_EMIT errored(except); } } auto file = wrapGFile(g_file_new_for_uri(FileUtils::urlEncode(m_uri).toUtf8().constData())); auto info = wrapGFileInfo(g_file_query_info(file.get()->get(), "*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, getCancellable().get()->get(), nullptr)); bool is_local_desktop_file = false; QUrl url = m_uri; //change the content of .desktop file; if (url.isLocalFile() && m_uri.endsWith(".desktop")) { GDesktopAppInfo *desktop_info = g_desktop_app_info_new_from_filename(url.path().toUtf8().constData()); if (G_IS_DESKTOP_APP_INFO(desktop_info)) { bool is_executable = g_file_test (url.path().toUtf8().constData(), G_FILE_TEST_IS_EXECUTABLE); is_local_desktop_file = is_executable; if (is_executable) { //rename the generic name GKeyFile *key_file = g_key_file_new(); g_key_file_load_from_file(key_file, url.path().toUtf8().constData(), G_KEY_FILE_KEEP_COMMENTS, nullptr); QString locale_name = QLocale::system().name(); QString local_generic_name_key = QString("Name[%1]").arg(locale_name); if (g_key_file_has_key(key_file, G_KEY_FILE_DESKTOP_GROUP, local_generic_name_key.toUtf8().constData(), nullptr)) { //qDebug() << "local_generic_name_key:" <get(), info.get()->get(), G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, &set_err); if (set_err) { qDebug()<message; g_error_free(set_err); } g_key_file_free(key_file); } g_object_unref(desktop_info); } } QString targetName = m_new_name; if (is_local_desktop_file) { targetName = m_new_name+".desktop"; } auto parent = FileUtils::getFileParent(file); auto newFile = FileUtils::resolveRelativePath(parent, targetName); getOperationInfo().get()->m_dest_dir_uri = FileUtils::getFileUri(newFile); if (is_local_desktop_file) { fallback_retry: GError *err = nullptr; g_file_move(file.get()->get(), newFile.get()->get(), G_FILE_COPY_ALL_METADATA, nullptr, nullptr, nullptr, &err); if (err) { qDebug()<message; FileOperationError except; except.srcUri = m_uri; except.destDirUri = FileUtils::getFileUri(newFile); except.isCritical = true; except.op = FileOpRename; except.title = tr("Rename file error"); except.errorType = ET_GIO; except.errorCode = err->code; except.errorStr = err->message; if (G_IO_ERROR_EXISTS == err->code) { except.dlgType = ED_CONFLICT; auto responseType = except.respCode; switch (responseType) { case Retry: goto fallback_retry; case Cancel: cancel(); break; default: break; } } else { except.dlgType = ED_WARNING; auto responseType = except.respCode; switch (responseType) { case Retry: goto fallback_retry; case Cancel: cancel(); break; default: break; } } } } else { retry: GError* err = nullptr; FileOperationError except; except.srcUri = m_uri; except.errorType = ET_GIO; except.op = FileOpRename; except.dlgType = ED_WARNING; except.title = tr("Rename file error"); except.destDirUri = FileUtils::getFileUri(newFile); qDebug() << "rename: " << g_file_get_uri(newFile.get()->get()); g_autofree char* newName = g_file_get_basename(newFile.get()->get()); g_file_set_display_name(file.get()->get(), newName, nullptr, &err); if (err) { except.errorCode = err->code; except.errorStr = err->message; qDebug() << err->message; if (G_IO_ERROR_EXISTS == err->code) { ExceptionResponse resp = prehandle(err); if (Other == resp) { except.dlgType = ED_CONFLICT; except.errorCode = G_IO_ERROR_EXISTS; } Q_EMIT errored(except); resp = except.respCode; switch (resp) { case BackupAll: setAutoBackup(); case BackupOne:{ while (FileUtils::isFileExsit(g_file_get_uri(newFile.get()->get()))) { QString fileUri = handleDuplicate(FileUtils::getFileUri(newFile)); newFile = FileUtils::resolveRelativePath(parent, FileUtils::getUriBaseName(fileUri)); getOperationInfo().get()->m_dest_dir_uri = FileUtils::getFileUri(newFile); } goto retry; } case OverWriteAll: setAutoOverwrite(); case OverWriteOne: g_file_delete(newFile.get()->get(), nullptr, nullptr); goto retry; case IgnoreAll: setAutoIgnore(); case IgnoreOne: break; case Cancel: cancel(); goto cancel; default: break; } } else { Q_EMIT errored(except); switch (except.respCode) { case Retry: goto retry; case Cancel: cancel(); break; default: break; } } g_error_free(err); } } getOperationInfo().get()->m_dest_dir_uri = FileUtils::getFileUri(newFile); cancel: if (!isCancelled()) { auto string = g_file_get_uri(newFile.get()->get()); destUri = string; if (string) g_free(string); m_info->m_node_map.insert(m_uri, destUri); m_info->m_newname = m_new_name; m_info->m_oldname = m_old_name; } fileSync(m_uri, destUri); Q_EMIT operationFinished(); //notifyFileWatcherOperationFinished(); } ExceptionResponse FileRenameOperation::prehandle(GError *err) { if (err && G_IO_ERROR_EXISTS == err->code) { return m_apply_all; } return Other; } peony/libpeony-qt/file-operation/file-node.h0000644000175000017500000001326714205101223020022 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILENODE_H #define FILENODE_H #include #include #include #include #include "file-operation.h" namespace Peony { class FileNodeReporter; /*! * \brief The FileNode class *
* This class contains 3 parts: * 1. the self info, such as uri, basename, etc, * 2. the parent node and list of children, * 3. the reporter handle. *
*
* FileNode is smilar to FileItem, but it is more simple. It is used with many limitation. * Cause it method is Synchronized, you should never try use this class in the ui-thread. * This class is desgined for file operation classes which running in other threads. * For example, a copy operation might need enumerate all children for get the total size * of the source files. *
*
* The FileNodeReporter handle is used for send signal to tell other object the current state * of file node enumeration. Actually, a FileNode instance always be with a FileNodeReproter * instance at its initialization. *
* \see FileNodeReporter. */ class PEONYCORESHARED_EXPORT FileNode { friend class FileNodeReporter; public: enum State { Unhandled, Handling, Handled, Cleared, /*! \deprecated */ Invalid }; FileNode(QString uri, FileNode* parent, FileNodeReporter *reporter = nullptr); ~FileNode(); //FIXME: do i need add cancel function? void findChildrenRecursively(); void computeTotalSize(goffset *offset); QString uri() { return m_uri; } QString destUri() { return m_dest_uri; } State state() { return m_state; } Peony::ExceptionResponse responseType() { return m_err_response; } QString baseName() { return m_basename; } const QString destBaseName() { return m_dest_basename; } FileNode *parent() { return m_parent; } QList *children() { return m_children; } qint64 size() { return m_size; } bool isFolder() { return m_is_folder; } /*! * \brief getRelativePath * \return * \deprecated */ QString getRelativePath(); /*! * \brief setDestUri * \param uri, the raw source uri of this file. * \deprecated * this method should not be used in newly writen code. * * \details *
* In peony-qt, operantion is cancellable. That means we need add rollback function * for handling cancelled operation, to make sure that the operation is completed. * We should create a list of node trees for the files which have been copied or moved * for 'recover' them to the previous uri. * Dest uri is set when the file has been copied or moved to dest location. * This will aslo changed the node states. The rollback function will determine how to roll back * based on the status of dest uri and current states. *
* \see setState(). */ void setDestUri(QString uri) { m_dest_uri = uri; } /*! * \brief setState * \param state *
* State represent the current state of file. *
* \details * States of file has 3 types, there are: Unhandled, Handled, and Cleared. * Usually a file operaion of a file just have 2 states, Unhandled and Handled. * But some multi-step operation, such as fallback move operation, could have * 3 states. The file state will be changed when the file has been moved, copied * or deleted. That will guide the application how to roll back if the operation * was cancelled. */ void setState(State state) { m_state = state; } /*! * \brief setErrorResponse * \param type * \details * When a file get into error in executing g_file operation, it will get the error handle response form * FileOperationErrorHandler. the when the file operation start clearing and rollbacking, the response type * will guide them how to do that. * For example, if a g_file move operation is ignored, it will not be cleared when clearing. */ void setErrorResponse(ExceptionResponse type) { m_err_response = type; } void setDestFileName(const QString &name) { m_dest_basename = name; if (nullptr != m_dest_uri) { QStringList ls = m_dest_uri.split("/"); ls.pop_back(); ls.push_back(name); m_dest_uri = ls.join("/"); } } const QString resolveDestFileUri(const QString &destRootDir); private: QString m_uri = nullptr; QString m_basename = nullptr; QString m_dest_basename = nullptr; goffset m_size = 0; bool m_is_folder = false; FileNode *m_parent = nullptr; QList *m_children = nullptr; QString m_dest_uri = nullptr; State m_state = Unhandled; ExceptionResponse m_err_response = Other; FileNodeReporter *m_reporter = nullptr; }; } #endif // FILENODE_H peony/libpeony-qt/file-operation/file-trash-operation.h0000644000175000017500000000325714205115226022222 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILETRASHOPERATION_H #define FILETRASHOPERATION_H #include "peony-core_global.h" #include "file-operation.h" #include "file-node.h" #include "file-info.h" namespace Peony { class PEONYCORESHARED_EXPORT FileTrashOperation : public FileOperation { Q_OBJECT public: explicit FileTrashOperation(QStringList srcUris, QObject *parent = nullptr); std::shared_ptr getOperationInfo() override { return m_info; } void run() override; private: void forceDelete (QString uri); void setErrorMessage (GError** err); void deleteRecursively(FileNode *node); private: int m_current_count = 0; int m_total_count = 0; QStringList m_src_uris; std::shared_ptr m_info = nullptr; // record source file infos before trashed, only used in native fs QList> m_src_infos; }; } #endif // FILETRASHOPERATION_H peony/libpeony-qt/file-operation/file-operation-error-handler.h0000644000175000017500000000665414205101223023641 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILEOPERATIONERRORHANDLER_H #define FILEOPERATIONERRORHANDLER_H #include #include #include #include #include "gerror-wrapper.h" #include "peony-core_global.h" #define ErrorHandlerIID "org.ukui.peony-qt.FileOperationErrorHandler" namespace Peony { enum ExceptionType { ET_GIO, ET_CUSTOM, }; /*! * \brief Type of error handling * \li ED_CONFLICT: General conflict handling for file operations */ enum ExceptionDialogType { ED_WARNING, ED_CONFLICT, ED_NOT_SUPPORTED }; /*! * \brief All possible results of error handling */ enum ExceptionResponse { Other, Retry, Cancel, Rename, Invalid, IgnoreOne, IgnoreAll, BackupOne, BackupAll, OverWriteOne, OverWriteAll, Force, // Force operation ForceAll, // }; typedef enum{ FileOpInvalid, //invalid operation FileOpMove, //file or dir move FileOpCopy, //file or dir copy FileOpLink, //file or dir create link FileOpRename, //file or dir rename FileOpTrash, //file or dir delete to trash FileOpUntrash, //file or dir restore to origin from trash FileOpDelete, //file or dir delete forever FileOpCount, //file or dir file count FileOpCreateTemp, //create file or dir FileOpRenameToHideFile, // file or dir rename to a hide file FileOpNum, }FileOpsType; /*! * \brief The format of the data that needs to be transferred for error handling operations * \li errorCode: Error code * \li isCritical: is critical * \li title: The title that appears in the error handling window, indicating what operation went wrong * \li srcUri: The file/folder being operated on * \li destDirUri: The target folder * \li errorType: Error types, gio errors and custom errors * \li respCode: The action selected by the user * \li respValue: Data entered by the user */ typedef struct _FileOperationError { int errorCode; bool isCritical; FileOpsType op; QString title; QString srcUri; QString destDirUri; QString errorStr; ExceptionType errorType; ExceptionDialogType dlgType; ExceptionResponse respCode; QMap respValue; } FileOperationError; class PEONYCORESHARED_EXPORT FileOperationErrorHandler { public: virtual ~FileOperationErrorHandler() = 0; virtual void handle (FileOperationError& error) = 0; }; } Q_DECLARE_INTERFACE(Peony::FileOperationErrorHandler, ErrorHandlerIID) #endif // FILEOPERATIONERRORHANDLER_H peony/libpeony-qt/file-operation/file-operation.h0000644000175000017500000003000714205101223021064 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILEOPERATION_H #define FILEOPERATION_H #include #include #include #include #include "gerror-wrapper.h" #include "gobject-template.h" #include "peony-core_global.h" #include "file-operation-error-handler.h" #include "file-operation-manager.h" namespace Peony { class FileOperationInfo; /*! * \brief The FileOperation class *
* This class is an interface for several kinds of file operatrion, * like move, copy or delete, etc. *
* \details * FileOperation specifies a set of signals as an interactive interface. * There a mainly two kinds of interface, operation wizard and error handler. * Operation Wizard is used to show the operation progess and cancel the operation * in ui. Error Handler is used to handle the error when the operation went error. * \note * You should not use this class and derived classes in main thread. * Insteadly, using QThreadPool::start() is the best choice. * \see * FileOperationProgressWizard, FileOperationErrorHandler, FileOperationErrorDialog. * * \todo * add disk i/o speed computing function, this should be implement by implement * gio's progress callback. */ class PEONYCORESHARED_EXPORT FileOperation : public QObject, public QRunnable { friend class FileOperationManager; Q_OBJECT public: explicit FileOperation(QObject *parent = nullptr); ~FileOperation(); virtual void run(); void setHasError(bool hasError = true); bool hasError() { return m_has_error; } /*! * \brief getOperationInfo * \return * \details * This is a virtual function, some derived operation class should override * this function. * The FileOperation instance will destroy itself when it finished, but its info might not. * The FileOperationInfo is a part of peony-qt's undo/redo stack(s). FileOperationManager * will manage the stack(s) made up of these info. */ virtual std::shared_ptr getOperationInfo() { return nullptr; } /*! * \deprecated * \brief setShouldReversible * \param reversible * \details * If operation is reversible, it should support to be undo and redo. * \note * Even though a operation has been set should reversible, it doesn't mean * that it really reversible. For example, if a file was deleted, it can not * be undo, so it should not add into operation's history of undo/redo. * If you don't hope your operation keep records, just do not set it. */ void setShouldReversible(bool reversible = true) { m_reversible = reversible; } virtual bool reversible() { return m_reversible; } bool isCancelled() { return m_is_cancelled; } Q_SIGNALS: /*! * \brief invalidOperation * \param message * \details * Before a file operation start, peony-qt will do some simple checks. * If there is an obvious error, the operation will not be performed. * For example, copying/moving a file to the same folder, renaming a file * with existed name, etc. * The operation will send the invalidOperation() signal and skip the fileoperation. */ void invalidOperation(const QString &message); void invalidExited(const QString &message); /*! * \brief operationStarted *
* This signal should be sent when operation started. * when a derived class implement the run() method, it aslo need send this signal * to tell other object that the operation has started. it might use block-queue connect * for other object prepared. *
*/ void operationStarted(); /*! * \brief errored *
* This signal should be sent when a derived class instance went to an gio error. * The return value is needed by the instance for the error handling. *
* \param err, the shared_ptr wrapper of GError. * \param isCritical, the error is critical and operation should be interrupted now. * \return \retval response type for error handling. * \note Qt's signal/slot provide a blocking flag to ensure get return value of signal. * If you want to get response value rightly, you must connect this signal with * Qt::BlockingQueuedConnection flag set. That also limit you use fileoperation and its * derived class in main thread. */ void errored(FileOperationError& error); //int errored(const QString &srcUri, const QString &destUri, const Peony::GErrorWrapperPtr &err, bool isCritical = false); void FileProgressCallback(const QString &srcUri, const QString &destUri, const QString& icon, const qint64& current_file_offset, const qint64& current_file_size); /*! * \brief operationRequestShowWizard * \details * Instead of connect with operationStarted() signal, using operationRequestShowWizard() for showing * a wizard is better. Because you might not want to see a wizard in some operation. * \note * only fallback-move, copy and delete send this signal. */ void operationRequestShowWizard(); /*! * \brief operationFallbackRetried * \details * In peony-qt, we perfer user using native move function for fast moving. * Unfortunatly there are many limitration for native move. * If a native move is failed for those reasons, the move operation will * retry with copy/delete function as fallback, it might cost much more time. */ void operationFallbackRetried(); /*! * \brief operationPreparedOne * \param srcUri * \param destUri * \param size * \details * This signal should be sent when the operation found a file node. * The signal reciver will count the received signals count as the total source files count. * The total size should also be accumulated. */ void operationPreparedOne(const QString &srcUri, const qint64 &size); /*! * \brief operationPrepared *
* This signal should be sent when the operation ready to handle the files. * Before we really handle the files, we might need to do something preparing. * For example, a recursively enumerating. We should send this signal when these * works have been done. *
*/ void operationPrepared(); /*! * \brief operationProgressedOne * \param srcUri * \param destUri * \param size * \details * This signal should be sent when the operation progressed one files. * The receiver could use operationPreparedOne() and operationProgressedOne() * to compute the current progress for most of operations. */ void operationProgressedOne(const QString& srcUri, const QString &destUri, const qint64 &size); /*! * \brief operationProgressed *
* This signal should be sent when the operation is half-finished. * Some operation, such as move, might be splitted into 2 parts. * Copy and delete both spend a while. * If the other object doesn't care the next process of unfinished * operation, they can connect this signal instead of operationFinished() * signal to ignore the next progress, even thought the operation will * continue until it really completed. *
*/ void operationProgressed(); /*! * \brief operationRollbackedOne * \param destUri * \param srcUri * \details * This signal should be sent when the operation had been cancelled, * and one file was rollbacked to the state before operation executed. * \note * The rollback progress is difficult to count. * This would mean the progress inaccuracy */ /*! * \brief operationAfterProgressedOne * \param srcUri * \details * This signal is not necerssary by all operations. It is used by some * operations that cannot be implemented in one step. * For example, a non-native move operation will use recursive copy and delete method. * In this case the operationProgressedOne() and operationProgressed() signal would * only show the copy states. Use operationAfterProgressedOne() and operationAfterProgressed() * for tell others the progress of the move operation's clearing progress. */ void operationAfterProgressedOne(const QString &srcUri); /*! * \brief operationAfterProgressed * \details * This signal is not necerssary by all operations. * If a multi-step operation finished the last operation, it should be sent. */ void operationAfterProgressed(); /*! * \brief operationRollbackedOne * \param destUri * \param srcUri * \details * In peony-qt, if a file operation was cancelled, it not just simply cancel the operation. * In order to maintain the atomicity of the operation, the cancelled operation will try rolling * back to previous state. This signal should be sent when a file which had been handled rollbacked. */ void operationRollbackedOne(const QString &destUri, const QString &srcUri); /*! * \brief operationStartRollbacked *
* This signal is used to tell other object that * the operation has cancelled and rollbacked, not all operations * send should this signal. *
*/ void operationStartRollbacked(); /*! * \brief operationStartSnyc *
* This signal is used to tell other object that file operation has finished and start a sync * operation. *
*/ void operationStartSnyc(); /*! * \brief operationFinished *
* This signal is used to tell other object that the file operation has finished with data synced. * Usually, a progress dialog can connect this signal and close itself when the signal triggered. *
*/ void operationFinished(); /** * @brief operationPause *
* This signal tells the thread that the current file operation should be paused. *
*/ void operationPause(); /** * @brief operationStart *
* This signal tells the thread that the current file operation should resume from its pause. *
*/ void operationResume(); /** * @brief operationCancel *
* This signal tells the thread that the current user has chosen to cancel the operation. * this signal is used for custom copy operations to release the lock in the thread. *
*/ void operationCancel(); public Q_SLOTS: virtual void cancel(); protected: void fileSync (QString srcFile, QString destFile); bool nameIsValid (QString& uri); bool makeFileNameValidForDestFS (QString& srcPath, QString& destPath, QString* newFileName); GCancellableWrapperPtr getCancellable() { return m_cancellable_wrapper; } /*! * \brief notifyFileWatcherOperationFinished * tell views operation finished. */ void notifyFileWatcherOperationFinished(); protected: bool m_is_pause = false; private: bool m_has_error = false; bool m_reversible = false; bool m_is_cancelled = false; GCancellableWrapperPtr m_cancellable_wrapper = nullptr; }; } //Q_DECLARE_METATYPE(Peony::FileOperation::ResponseType) #endif // FILEOPERATION_H peony/libpeony-qt/file-operation/file-operation-manager.cpp0000644000175000017500000005476314205115226023056 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-operation-manager.h" #include "file-operation.h" #include "global-settings.h" #include #include #include #include #include "file-copy-operation.h" #include "file-delete-operation.h" #include "file-link-operation.h" #include "file-move-operation.h" #include "file-rename-operation.h" #include "file-trash-operation.h" #include "file-untrash-operation.h" #include "file-operation-error-dialog.h" #include "file-operation-progress-wizard.h" #include "file-watcher.h" #include "audio-play-manager.h" #include "properties-window.h" #include #include using namespace Peony; static FileOperationManager *global_instance = nullptr; FileOperationManager::FileOperationManager(QObject *parent) : QObject(parent) { m_allow_parallel = GlobalSettings::getInstance()->getValue(ALLOW_FILE_OP_PARALLEL).toBool(); qRegisterMetaType("Peony::GErrorWrapperPtr"); qRegisterMetaType("Peony::GErrorWrapperPtr&"); m_thread_pool = new QThreadPool(this); m_progressbar = FileOperationProgressBar::getInstance(); if (!m_allow_parallel) { //Imitating queue execution. m_thread_pool->setMaxThreadCount(1); } // connect(m_progressbar, &FileOperationProgressBar::canceled, [=] () { m_progressbar->removeAllProgressbar(); }); // 休眠检测 GDBusConnection* pconnection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); if (pconnection) { g_dbus_connection_signal_subscribe(pconnection, "org.freedesktop.login1", "org.freedesktop.login1.Manager", "PrepareForSleep", "/org/freedesktop/login1", NULL, G_DBUS_SIGNAL_FLAGS_NONE, systemSleep, this, NULL); } } FileOperationManager::~FileOperationManager() { } FileOperationManager *FileOperationManager::getInstance() { if (global_instance == nullptr) { global_instance = new FileOperationManager; } return global_instance; } void FileOperationManager::close() { disconnect(); deleteLater(); global_instance = nullptr; Q_EMIT closed(); } void FileOperationManager::setAllowParallel(bool allow) { m_allow_parallel = allow; if (allow) { m_thread_pool->setMaxThreadCount(9999); } else { m_thread_pool->setMaxThreadCount(1); } GlobalSettings::getInstance()->setValue(ALLOW_FILE_OP_PARALLEL, allow); } bool FileOperationManager::isAllowParallel() { return m_allow_parallel; } void FileOperationManager::startOperation(FileOperation *operation, bool addToHistory) { auto operationInfo = operation->getOperationInfo(); if (operationInfo.get()->operationType() == FileOperationInfo::Trash) { auto value = GlobalSettings::getInstance()->getValue("showTrashDialog"); if (value.isValid()) { if (value.toBool() == false) { goto start; } } // check dialog QMessageBox questionBox; questionBox.addButton(QMessageBox::Yes); questionBox.addButton(QMessageBox::No); questionBox.addButton(tr("No, go to settings"), QMessageBox::ActionRole); questionBox.setText(tr("Do you want to put selected %1 item(s) into trash?").arg(operationInfo.get()->sources().count())); auto result = questionBox.exec(); if (result != QMessageBox::Yes) { if (result != QMessageBox::No) { // settings QStringList uris; uris<<"trash:///"; auto propertyWindow = new PropertiesWindow(uris); propertyWindow->show(); } return; } } // do not add move operation between favorite:/// // FIXME: due to some desgin issues, we can not // support undo/redo with favorite:/// yet. that // should be fixed in the future. // dnd use copy move mode here between different // file system, so we should better check operation class, // not operation info type. if (dynamic_cast(operation)) { if (addToHistory) { for (QString uri : operationInfo.get()->sources()) { if (uri.startsWith("favorite:///")) { addToHistory = false; break; } } if (addToHistory) { for (QString uri : operationInfo.get()->dests()) { if (uri.startsWith("favorite:///")) { addToHistory = false; break; } } } } } start: QApplication::setQuitOnLastWindowClosed(false); connect(operation, &FileOperation::operationFinished, this, [=]() { operation->notifyFileWatcherOperationFinished(); auto settings = GlobalSettings::getInstance(); bool runbackend = settings->getInstance()->getValue(RESIDENT_IN_BACKEND).toBool(); QApplication::setQuitOnLastWindowClosed(!runbackend); QTimer::singleShot(1000, this, [=]() { int last_op_count = m_thread_pool->children().count(); if (last_op_count == 0) { if (qApp->allWidgets().isEmpty()) { if (!runbackend) { qApp->quit(); } } } }); }, Qt::BlockingQueuedConnection); bool allowParallel = m_allow_parallel; auto opType = operationInfo->operationType(); switch (opType) { case FileOperationInfo::Trash: case FileOperationInfo::Delete: { allowParallel = true; auto operationSrcs = operationInfo->sources(); auto currentOps = m_thread_pool->children(); QList ops; for (auto child : currentOps) { auto op = qobject_cast(child); auto opInfo = op->getOperationInfo(); { for (auto src : operationSrcs) { if (opInfo->sources().contains(src)) { Peony::AudioPlayManager::getInstance()->playWarningAudio(); //do not allow operation. QMessageBox::critical(nullptr, tr("Can't delete."), tr("You can't delete a file when" "the file is doing another operation")); return; } } } } break; } default: break; } // progress bar ProgressBar* proc = m_progressbar->addFileOperation(); if (nullptr == proc) { qDebug() << "malloc error!"; return; } // begin proc->connect(operation, &FileOperation::operationPreparedOne, proc, &ProgressBar::onElementFoundOne); proc->connect(operation, &FileOperation::operationPrepared, proc, &ProgressBar::onElementFoundAll); proc->connect(operation, &FileOperation::operationProgressedOne, proc, &ProgressBar::onFileOperationProgressedOne); proc->connect(operation, &FileOperation::FileProgressCallback, proc, &ProgressBar::updateProgress); proc->connect(operation, &FileOperation::operationProgressed, proc, &ProgressBar::onFileOperationProgressedAll); proc->connect(operation, &FileOperation::operationAfterProgressedOne, proc, &ProgressBar::onElementClearOne); proc->connect(operation, &FileOperation::operationAfterProgressed, proc, &ProgressBar::switchToRollbackPage); proc->connect(operation, &FileOperation::operationStartRollbacked, proc, &ProgressBar::switchToRollbackPage); proc->connect(operation, &FileOperation::operationRollbackedOne, proc, &ProgressBar::onFileRollbacked); proc->connect(operation, &FileOperation::operationStartSnyc, proc, &ProgressBar::onStartSync); proc->connect(operation, &FileOperation::operationFinished, proc, &ProgressBar::onFinished); proc->connect(proc, &ProgressBar::cancelled, operation, &FileOperation::cancel); proc->connect(proc, &ProgressBar::cancelled, operation, &FileOperation::operationCancel); proc->connect(proc, &ProgressBar::pause, operation, &FileOperation::operationPause); proc->connect(proc, &ProgressBar::resume, operation, &FileOperation::operationResume); operation->connect(operation, &FileOperation::errored, [=]() { operation->setHasError(true); }); operation->connect(operation, &FileOperation::errored, this, &FileOperationManager::handleError, Qt::BlockingQueuedConnection); operation->connect(operation, &FileOperation::operationFinished, this, [=](){ Q_EMIT this->operationFinished(operation->getOperationInfo(), !operation->hasError()); if (operation->hasError()) { this->clearHistory(); return ; } if (addToHistory) { auto info = operation->getOperationInfo(); if (!info) return; if (info->operationType() != FileOperationInfo::Delete) { m_undo_stack.push(info); m_redo_stack.clear(); } else { this->clearHistory(); } } }, Qt::BlockingQueuedConnection); if (!allowParallel) { if (m_thread_pool->activeThreadCount() > 0) { QMessageBox::warning(nullptr, tr("File Operation is Busy"), tr("There have been one or more file" "operation(s) executing before. Your" "operation will wait for executing" "until it/them done. If you really " "want to execute file operations " "parallelly anyway, you can change " "the default option \"Allow Parallel\" " "in option menu.")); } operation->setParent(m_thread_pool); m_thread_pool->start(operation); } else { operation->setParent(m_thread_pool); m_thread_pool->start(operation); } Q_EMIT this->operationStarted(operation->getOperationInfo()); m_progressbar->showDelay(); } void FileOperationManager::startUndoOrRedo(std::shared_ptr info) { FileOperation *op = nullptr; switch (info->m_type) { case FileOperationInfo::Copy: { op = new FileCopyOperation(info->m_src_uris, info->m_dest_dir_uri); break; } case FileOperationInfo::Delete: { if (info->m_node_map.isEmpty()) op = new FileDeleteOperation(info->m_src_uris); else op = new FileDeleteOperation(info->m_node_map.keys()); break; } case FileOperationInfo::Link: { op = new FileLinkOperation(info->m_src_uris.at(0), info->m_dest_dir_uri); break; } case FileOperationInfo::Move: { op = new FileMoveOperation(info->m_src_uris, info->m_dest_dir_uri); auto moveOp = qobject_cast(op); moveOp->setAction(info->m_drop_action); break; } case FileOperationInfo::Rename: { if (info->m_node_map.isEmpty()) { op = new FileRenameOperation(info->m_src_uris.isEmpty()? nullptr: info->m_src_uris.at(0), info->m_dest_dir_uri); } else { op = new FileRenameOperation(info->m_node_map.firstKey(), info.get()->m_newname); } break; } case FileOperationInfo::Trash: { op = new FileTrashOperation(info->m_src_uris); break; } case FileOperationInfo::Untrash: { op = new FileUntrashOperation(info->m_src_uris); break; } default: break; } //do not record the undo/redo operation to history again. //this had been handled at undo() and redo() yet. //FIXME: if an undo/redo work went error (usually won't), //should i remove the operation info from stack? if (op) { startOperation(op, false); } } bool FileOperationManager::canUndo() { return !m_undo_stack.isEmpty(); } bool FileOperationManager::canRedo() { return !m_redo_stack.isEmpty(); } std::shared_ptr FileOperationManager::getUndoInfo() { return m_undo_stack.top(); } std::shared_ptr FileOperationManager::getRedoInfo() { return m_redo_stack.top(); } void FileOperationManager::undo() { if(!canUndo()) return; auto undoInfo = m_undo_stack.pop(); m_redo_stack.push(undoInfo); auto oppositeInfo = undoInfo->getOppositeInfo(undoInfo.get()); startUndoOrRedo(oppositeInfo); } void FileOperationManager::redo() { if (!canRedo()) return; auto redoInfo = m_redo_stack.pop(); m_undo_stack.push(redoInfo); startUndoOrRedo(redoInfo); } void FileOperationManager::clearHistory() { m_undo_stack.clear(); m_redo_stack.clear(); } void FileOperationManager::onFilesDeleted(const QStringList &uris) { qDebug()<isHidden()) { m_progressbar->m_error = true; } auto dialog = dynamic_cast(handle); if (dialog) { dialog->setProperty("useCustomShadow", true); dialog->setProperty("customShadowDarkness", 0.5); dialog->setProperty("customShadowWidth", 30); dialog->setProperty("customShadowRadius", QVector4D(1, 1, 1, 1)); dialog->setProperty("customShadowMargins", QVector4D(30, 30, 30, 30)); } handle->handle(error); delete handle; m_progressbar->m_error = false; m_progressbar->showDelay(300); } } void FileOperationManager::registerFileWatcher(FileWatcher *watcher) { m_watchers<m_src_dir_uri == QStandardPaths::writableLocation(QStandardPaths::TempLocation)) { return; } for (auto watcher : m_watchers) { if (!watcher->supportMonitor()) { auto srcDir = info->m_src_dir_uri; auto destDir = info->m_dest_dir_uri; auto firstUri = info->m_src_uris.first(); //'file:///run/user/1000/gvfs/smb-share:server=xxx,share=xxx/' converted to 'smb://xxx' GFile * file = g_file_new_for_uri(destDir.toLatin1().data()); char *uri = g_file_get_uri(file); if (uri) { destDir = uri; } g_object_unref(file); g_free(uri); if (info->operationType() == FileOperationInfo::Link || info->operationType() == FileOperationInfo::Rename) { srcDir = FileUtils::getParentUri(firstUri); } if ("" == srcDir) { if (firstUri.endsWith("/")) { firstUri.chop(1); } QStringList fileSplit = firstUri.split("/"); fileSplit.pop_back(); srcDir = fileSplit.join("/"); } // check watcher directory // srcDir is null in samba filesystem, so that it not work // currentUri maybe is 'file:///run/user/1000/gvfs/smb-share:server=xxx,share=xxx/' or 'smb://xxx' if (watcher->currentUri() == srcDir || watcher->currentUri() == destDir) { // tell the view/model the directory should be updated watcher->requestUpdateDirectory(); } qDebug() << "src:" << srcDir << " == dest:" << destDir << " == type:" << info->operationType(); if (srcDir.startsWith("smb://") || srcDir.startsWith("ftp://") || srcDir.startsWith("sftp://") || destDir.startsWith("smb://") || destDir.startsWith("ftp://") || destDir.startsWith("sftp://")) { watcher->requestUpdateDirectory(); } } } } //FIXME: get opposite info correcty. FileOperationInfo::FileOperationInfo(QStringList srcUris, QString destDirUri, Type type, QObject *parent): QObject(parent) { m_src_uris = srcUris; m_dest_dir_uri = destDirUri; oppositeInfoConstruct(type); } //FIXME: get opposite info correcty. FileOperationInfo::FileOperationInfo(QStringList srcUris, QStringList destDirUris, Type type, QObject *parent): QObject(parent) { m_src_uris = srcUris; m_dest_dir_uris = destDirUris; oppositeInfoConstruct(type); } void FileOperationInfo::oppositeInfoConstruct(Type type) { m_type = type; switch (type) { case Move: { m_opposite_type = Move; commonOppositeInfoConstruct(); break; } case Trash: { m_opposite_type = Untrash; trashOppositeInfoConstruct(); break; } case Untrash: { m_opposite_type = Trash; UntrashOppositeInfoConstruct(); break; } case Delete: { m_opposite_type = Other; break; } case Copy: { m_opposite_type = Delete; commonOppositeInfoConstruct(); break; } case Rename: { m_opposite_type = Rename; RenameOppositeInfoConstruct(); break; } case Link: { m_opposite_type = Delete; LinkOppositeInfoConstruct(); break; } case CreateTxt: { m_opposite_type = Delete; commonOppositeInfoConstruct(); break; } case CreateFolder: { m_opposite_type = Delete; commonOppositeInfoConstruct(); break; } case CreateTemplate: { m_opposite_type = Delete; commonOppositeInfoConstruct(); break; } default: { m_opposite_type = Other; } } return; } void FileOperationInfo::commonOppositeInfoConstruct() { for (auto srcUri : m_src_uris) { auto srcFile = wrapGFile(g_file_new_for_uri(srcUri.toUtf8().constData())); if (m_src_dir_uri.isNull()) { auto srcParent = FileUtils::getFileParent(srcFile); m_src_dir_uri = FileUtils::getFileUri(srcParent); } QString relativePath = FileUtils::getFileBaseName(srcFile); auto destDirFile = wrapGFile(g_file_new_for_uri(m_dest_dir_uri.toUtf8().constData())); auto destFile = FileUtils::resolveRelativePath(destDirFile, relativePath); QString destUri = FileUtils::getFileUri(destFile); m_dest_uris< FileOperationInfo::getOppositeInfo(FileOperationInfo *info) { auto oppositeInfo = std::make_shared(info->m_dest_uris, info->m_src_dir_uri, m_opposite_type); if (info->m_drop_action == Qt::TargetMoveAction) { oppositeInfo->m_drop_action = Qt::TargetMoveAction; oppositeInfo->m_type = FileOperationInfo::Move; } QMap oppsiteMap; for (auto key : m_node_map.keys()) { auto value = m_node_map.value(key); oppsiteMap.insert(value, key); } oppositeInfo->m_node_map = oppsiteMap; oppositeInfo->m_newname = this->m_oldname; oppositeInfo->m_oldname = this->m_newname; return oppositeInfo; } void FileOperationManager::systemSleep (GDBusConnection *connection, const gchar *senderName, const gchar *objectPath, const gchar *interfaceName, const gchar *signalName, GVariant *parameters, gpointer udata) { FileOperationProgressBar* pb = static_cast(udata)->m_progressbar; if (pb) { Q_EMIT pb->pause(); } Q_UNUSED(connection) Q_UNUSED(senderName) Q_UNUSED(objectPath) Q_UNUSED(signalName) Q_UNUSED(parameters) Q_UNUSED(interfaceName) } peony/libpeony-qt/file-operation/file-operation-progress-wizard.h0000644000175000017500000001224514205101223024230 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILEOPERATIONPROGRESSWIZARD_H #define FILEOPERATIONPROGRESSWIZARD_H #include #include "peony-core_global.h" class QLabel; class QProgressBar; class QFormLayout; class QGridLayout; class QSystemTrayIcon; namespace Peony { class FileOperationPreparePage; class FileOperationProgressPage; class FileOperationAfterProgressPage; class FileOperationRollbackPage; /*! * \brief The FileOperationProgressWizard class *
* This class is an graphic user interface of serveral file operations. * There are two page, the preparing page and the progress page. * The preparing page is used to count the source files need to be handled. * And the progress page is used to show the current progress of the operation. *
* \note * This is the common interface of all kinds of file operation. If you want to * implement a special interface for one kind operation. you can dervied the class * or re-write a new interface and re-implement the process connect to the operation. */ class PEONYCORESHARED_EXPORT FileOperationProgressWizard : public QWizard { friend class FileOperationPreparePage; friend class FileOperationProgressPage; friend class FileOperationAfterProgressPage; friend class FileOperationRollbackPage; Q_OBJECT public: enum PageId { PreparePage, ProgressPage }; explicit FileOperationProgressWizard(QWidget *parent = nullptr); ~FileOperationProgressWizard() override; Q_SIGNALS: void cancelled(); public Q_SLOTS: virtual void delayShow(); virtual void switchToPreparedPage(); virtual void onElementFoundOne(const QString &uri, const qint64 &size); virtual void onElementFoundAll(); virtual void switchToProgressPage(); virtual void onFileOperationProgressedOne(const QString &uri, const QString &destUri, const qint64 &size); virtual void onFileOperationProgressedAll(); virtual void switchToAfterProgressPage(); virtual void onElementClearOne(const QString &uri); virtual void switchToRollbackPage(); virtual void onFileRollbacked(const QString &destUri, const QString &srcUri); virtual void updateProgress(const QString &srcUri, const QString &destUri, quint64 current, quint64 total); virtual void onStartSync(); protected: void closeEvent(QCloseEvent *e) override; qint64 m_total_size = 0; int m_current_size = 0; int m_total_count = 0; int m_current_count = 1; FileOperationPreparePage *m_first_page = nullptr; FileOperationProgressPage *m_second_page = nullptr; FileOperationAfterProgressPage *m_third_page = nullptr; FileOperationRollbackPage *m_last_page = nullptr; private: QSystemTrayIcon *m_tray_icon = nullptr; QTimer *m_delayer; QTimer *m_tip_delayer; }; class PEONYCORESHARED_EXPORT FileOperationPreparePage : public QWizardPage { friend class FileOperationProgressWizard; Q_OBJECT public: explicit FileOperationPreparePage(QWidget *parent = nullptr); ~FileOperationPreparePage(); Q_SIGNALS: void cancelled(); private: QFormLayout *m_layout = nullptr; QLabel *m_src_line = nullptr; QLabel *m_state_line = nullptr; }; class PEONYCORESHARED_EXPORT FileOperationProgressPage : public QWizardPage { friend class FileOperationProgressWizard; Q_OBJECT public: explicit FileOperationProgressPage(QWidget *parent = nullptr); ~FileOperationProgressPage(); Q_SIGNALS: void cancelled(); private: QGridLayout *m_layout = nullptr; QLabel *m_src_line = nullptr; QLabel *m_dest_line = nullptr; QLabel *m_state_line = nullptr; QProgressBar *m_progress_bar = nullptr; }; class PEONYCORESHARED_EXPORT FileOperationAfterProgressPage : public QWizardPage { friend class FileOperationProgressWizard; Q_OBJECT public: explicit FileOperationAfterProgressPage(QWidget *parent = nullptr); ~FileOperationAfterProgressPage(); private: QGridLayout *m_layout = nullptr; QLabel *m_src_line = nullptr; QProgressBar *m_progress_bar = nullptr; int m_file_deleted_count = 0; }; class PEONYCORESHARED_EXPORT FileOperationRollbackPage : public QWizardPage { Q_OBJECT friend class FileOperationProgressWizard; public: explicit FileOperationRollbackPage(QWidget *parent = nullptr); ~FileOperationRollbackPage(); private: QGridLayout *m_layout = nullptr; QProgressBar *m_progress_bar = nullptr; int m_current_count = 0; }; } #endif // FILEOPERATIONPROGRESSWIZARD_H peony/libpeony-qt/file-operation/file-move-operation.h0000644000175000017500000001506714205101223022041 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILEMOVEOPERATION_H #define FILEMOVEOPERATION_H #include "file-operation.h" #include "file-info.h" namespace Peony { class FileNodeReporter; class FileNode; class FileOperationInfo; /*! * \brief The FileMoveOperation class * \todo * implement the backup option of err handling. */ class PEONYCORESHARED_EXPORT FileMoveOperation : public FileOperation { Q_OBJECT public: /*! * \brief FileMoveOperation * \param sourceUris, list of source files' uris. * \param destUri, dest folder uri. * \param parent *
* This class is derived from FileOperation class, implement the virtual * run() function for moving files to a target directory. *
* \note * Unlike the g_file_move function, the target file of this operation must * be a directory. */ explicit FileMoveOperation(QStringList sourceUris, QString destDirUri, QObject *parent = nullptr); ~FileMoveOperation() override; /*! * \brief setCopyMove * \param copyMove * \details * If a move operation is not native move, it will do a copy and delete operation * as a 'move'. setCopyMove will tell the operation do not delete the source files. */ void setCopyMove(bool copyMove = true); void setAction(Qt::DropAction action); /*! * \brief setForceUseFallback * \param useFallback * \value true, force using copy and delete replace native move function provided by file system. * \value false, try using native move function first. *
* The reason why we have a optional of force-using fallback is that we might want to get * more details of a move, which is the native one doesn't support. * If we force using fallback function, we will trying to get infos of files that waiting for * moving. So that we can count the total size, and dynamicly get the progress of operation. *
*/ void setForceUseFallback(bool useFallback = true) { m_force_use_fallback = useFallback; } /*! * \brief rollbackNodeRecursively * \param node, the parent node need rollback *
* This is a recursive method for internal operation rollback. * recursive rollback function is more complex than recursive copy and delete function, * because we need decied the order of rollback and recursion by the node's states. * If the node need rollback state is Handled (cancelled in progressing, such as copy), * we need do recursion first, so that we can delete sub nodes successfully. * If the node state is Cleared, this will be the opposite of the previous one. * The rollback file will be created first. * <\br> * \note A native move operation does not support recursive rollback. */ void rollbackNodeRecursively(FileNode *node); void run() override; std::shared_ptr getOperationInfo() override { return m_info; } public Q_SLOTS: void cancel() override; protected: static void progress_callback(goffset current_num_bytes, goffset total_num_bytes, FileMoveOperation *p_this); void copyRecursively(FileNode *node); void deleteRecursively(FileNode *node); bool isValid(); void move(); void moveForceUseFallback(); void moveForceUseFallback(FileNode* node); /*! * \brief prehandle * \param err * \return * \retval true if error handled. * \retval false if error not handled. * \details * There's something complicate handling the error in file operation. * User might handle the same kind of errors unified. * But different errors actually need different way to solve. * this function will help us do some thing prehandling if user * choose IgnoreAll, BackupAll or OverWriteAll option when a * specific type error occured. */ ExceptionResponse prehandle(GError *err); private: QStringList m_source_uris; QString m_dest_dir_uri = nullptr; /*! * \brief m_current_count, used in progress_callback */ int m_current_count = 0; /*! * \brief m_total_count, used in progress_callback */ int m_total_count = 0; /*! * \brief m_current_src_uri, used in progress_callback. */ QString m_current_src_uri = nullptr; /*! * \brief m_current_dest_dir_uri, used in progress_callback. */ QString m_current_dest_dir_uri = nullptr; goffset m_current_offset = 0; goffset m_total_szie = 0; /*! * \brief m_force_use_callback * \value true, the move operation will use copy + delete fallback anyway. * \value false, the move operation will priority using native move operations * if supported. The native implementation may support moving directories * (for instance on moves inside the same filesystem). */ bool m_force_use_fallback = false; /*! * \brief m_copy_move * \value true, if native move operation is not supported. * just copy source files and do not delete source files. * \deprecated * should be dropped. use m_move_action instead. */ bool m_copy_move = false; /*! * \brief m_move_action * replacing m_copy_move flag. */ Qt::DropAction m_move_action = Qt::MoveAction; GFileCopyFlags m_default_copy_flag = GFileCopyFlags(G_FILE_COPY_NOFOLLOW_SYMLINKS| G_FILE_COPY_NO_FALLBACK_FOR_MOVE); FileNodeReporter *m_reporter = nullptr; /*! * \brief m_prehandle_hash * \details * Once a move operation get into error, this class might cache the specific response * for next prehandleing. */ QHash m_prehandle_hash; std::shared_ptr m_info = nullptr; }; } #endif // FILEMOVEOPERATION_H peony/libpeony-qt/file-operation/file-copy-operation.cpp0000644000175000017500000006377114205115226022415 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-copy-operation.h" #include "file-node-reporter.h" #include "file-node.h" #include "file-enumerator.h" #include "file-info.h" #include "file-utils.h" #include "file-operation-manager.h" #include "clipboard-utils.h" #include #include #include "file-copy.h" #include using namespace Peony; static void handleDuplicate(FileNode *node) { node->setDestFileName(FileUtils::handleDuplicateName(node->destBaseName())); } FileCopyOperation::FileCopyOperation(QStringList sourceUris, QString destDirUri, QObject *parent) : FileOperation (parent) { QUrl destDirUrl = Peony::FileUtils::urlEncode(destDirUri); QUrl firstSrcUrl = Peony::FileUtils::urlEncode(sourceUris.first()); if (destDirUrl.isParentOf(firstSrcUrl)) { m_is_duplicated_copy = true; }/* else { // fix #83068 // windows里的重复复制操作没有备份选项,但是会一直弹框提示,这里和windows的行为靠拢 auto lastPasteDirectoryUri = ClipboardUtils::getInstance()->getLastTargetDirectoryUri(); QUrl lastPasteDirectoryUrl = Peony::FileUtils::urlEncode(lastPasteDirectoryUri); if (destDirUrl == lastPasteDirectoryUrl) { m_is_duplicated_copy = true; } }*/ m_conflict_files.clear(); m_source_uris = sourceUris; m_dest_dir_uri = FileUtils::urlDecode(destDirUri); m_reporter = new FileNodeReporter; connect(m_reporter, &FileNodeReporter::nodeFound, this, &FileOperation::operationPreparedOne); m_info = std::make_shared(sourceUris, destDirUri, FileOperationInfo::Copy); } FileCopyOperation::~FileCopyOperation() { delete m_reporter; m_conflict_files.clear(); } ExceptionResponse FileCopyOperation::prehandle(GError *err) { setHasError(true); switch (err->code) { 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: return Other; } if (G_IO_ERROR_EXISTS == err->code && m_is_duplicated_copy) { return BackupAll; } if (m_prehandle_hash.contains(err->code)) return m_prehandle_hash.value(err->code); return Other; } void FileCopyOperation::progress_callback(goffset current_num_bytes, goffset total_num_bytes, FileCopyOperation *p_this) { if (total_num_bytes < current_num_bytes) return; QUrl url(Peony::FileUtils::urlEncode(p_this->m_current_src_uri)); auto currnet = p_this->m_current_offset + current_num_bytes; auto total = p_this->m_total_szie; auto fileIconName = FileUtils::getFileIconName(p_this->m_current_src_uri, false); auto destFileName = FileUtils::isFileDirectory(p_this->m_current_dest_dir_uri) ? p_this->m_current_dest_dir_uri + "/" + url.fileName() : p_this->m_current_dest_dir_uri; // qDebug()<FileProgressCallback(p_this->m_current_src_uri, destFileName, fileIconName, currnet, total); } void FileCopyOperation::copyRecursively(FileNode *node) { if (isCancelled()) return; node->setState(FileNode::Handling); QString destName = ""; fallback_retry: QString destFileUri = node->resolveDestFileUri(m_dest_dir_uri); QUrl destFileUrl = Peony::FileUtils::urlEncode(destFileUri); node->setDestUri(destFileUri); QString srcUri = node->uri(); qDebug()<<"dest file uri:"<uri(); m_current_dest_dir_uri = destFileUri; if (node->isFolder()) { GError *err = nullptr; //NOTE: mkdir doesn't have a progress callback. g_file_make_directory(destFile.get()->get(), getCancellable().get()->get(), &err); if (err) { FileOperationError except; if (err->code == G_IO_ERROR_CANCELLED) { return; } auto errWrapperPtr = GErrorWrapper::wrapFrom(err); int handle_type = prehandle(err); except.errorType = ET_GIO; except.srcUri = m_current_src_uri; except.destDirUri = m_current_dest_dir_uri; except.op = FileOpCopy; except.title = tr("File copy error"); except.errorCode = err->code; if (handle_type == Other) { if (G_IO_ERROR_EXISTS == err->code) { except.dlgType = ED_CONFLICT; Q_EMIT errored(except); auto typeData = except.respCode; handle_type = typeData; } else { except.dlgType = ED_WARNING; Q_EMIT errored(except); auto typeData = except.respCode; handle_type = typeData; } } //handle. switch (handle_type) { case IgnoreOne: { node->setState(FileNode::Unhandled); node->setErrorResponse(IgnoreOne); break; } case IgnoreAll: { node->setState(FileNode::Unhandled); node->setErrorResponse(IgnoreOne); m_prehandle_hash.insert(err->code, IgnoreOne); break; } case OverWriteOne: { node->setState(FileNode::Handled); node->setErrorResponse(OverWriteOne); //make dir has no overwrite break; } case OverWriteAll: { node->setState(FileNode::Handled); node->setErrorResponse(OverWriteOne); m_prehandle_hash.insert(err->code, OverWriteOne); break; } case BackupOne: { node->setState(FileNode::Handled); node->setErrorResponse(BackupOne); // use custom name QString name = ""; QStringList extendStr = node->destBaseName().split("."); if (extendStr.length() > 0) { extendStr.removeAt(0); } QString endStr = extendStr.join("."); if (except.respValue.contains("name")) { name = except.respValue["name"].toString(); if (endStr != "" && name.endsWith(endStr)) { node->setDestFileName(name); } else if ("" != endStr && "" != name) { node->setDestFileName(name + "." + endStr); } } while (FileUtils::isFileExsit(node->resolveDestFileUri(m_dest_dir_uri))) { handleDuplicate(node); } goto fallback_retry; } case BackupAll: { node->setState(FileNode::Handled); node->setErrorResponse(BackupOne); while (FileUtils::isFileExsit(node->resolveDestFileUri(m_dest_dir_uri))) { handleDuplicate(node); } //make dir has no backup m_prehandle_hash.insert(err->code, BackupOne); goto fallback_retry; } case Retry: { goto fallback_retry; } case Cancel: { node->setState(FileNode::Unhandled); cancel(); break; } default: break; } } else { node->setState(FileNode::Handled); } //assume that make dir finished anyway m_current_offset += node->size(); Q_EMIT operationProgressedOne(node->uri(), node->destUri(), node->size()); for (auto child : *(node->children())) { copyRecursively(child); } } else { GError *err = nullptr; QUrl url = node->uri(); bool is_desktop_file = false; g_autoptr(GFile) src = g_file_new_for_uri(node->uri().toUtf8().constData()); g_autoptr(GFileInfo) srcInfo = nullptr; if (src) { srcInfo = g_file_query_info(src, "unix::*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); } if (url.isLocalFile() && node->uri().endsWith(".desktop")) { GDesktopAppInfo* desktop_info = g_desktop_app_info_new_from_filename(url.path().toUtf8().constData()); if (G_IS_DESKTOP_APP_INFO(desktop_info)) { is_desktop_file = true; GKeyFile* key_file = g_key_file_new(); QRegExp regExp (QString("\\ -\\ %1\\(\\d+\\)(\\.[0-9a-zA-Z\\.]+|)$").arg(QObject::tr("duplicate"))); g_key_file_load_from_file(key_file, url.path().toUtf8().constData(), G_KEY_FILE_KEEP_COMMENTS, nullptr); QString locale_name = QLocale::system().name(); QString local_generic_name_key = QString("Name[%1]").arg(locale_name); GError* error = NULL; if (g_key_file_has_key(key_file, G_KEY_FILE_DESKTOP_GROUP, local_generic_name_key.toUtf8().constData(), nullptr)) { g_autofree char* val = g_key_file_get_value(key_file, G_KEY_FILE_DESKTOP_GROUP, local_generic_name_key.toUtf8().constData(), &error); if (error) { qWarning() << "get desktop file:" << node->uri() << " name error:" << error->code << " -- " << error->message; g_error_free(error); error = nullptr; } else { if (node->destBaseName().contains(regExp)) { QString name1 = regExp.cap(0).replace(".desktop", ""); QString name = QString("%1%2.desktop").arg(val).arg(name1); node->setDestFileName(name); } else { QString name = QString("%1 - %2(1).desktop").arg(val).arg(QObject::tr("duplicate")); node->setDestFileName(name); } } } else if (g_key_file_has_key(key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, nullptr)) { g_autofree char* val = g_key_file_get_value(key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, &error); if (error) { qWarning() << "get desktop file:" << node->uri() << " name error:" << error->code << " -- " << error->message; g_error_free(error); error = nullptr; } else { if (node->destBaseName().contains(regExp)) { QString name1 = regExp.cap(0).replace(".desktop", ""); QString name = QString("%1%2.desktop").arg(val).arg(name1); node->setDestFileName(name); } else { QString name = QString("%1 - %2(1).desktop").arg(val).arg(QObject::tr("duplicate")); node->setDestFileName(name); } } } g_key_file_free(key_file); g_object_unref(desktop_info); } } FileCopy fileCopy (node->uri(), destFileUri, m_default_copy_flag, getCancellable().get()->get(), GFileProgressCallback(progress_callback), this, &err); if (m_is_pause) fileCopy.pause(); fileCopy.connect(this, &FileOperation::operationPause, &fileCopy, &FileCopy::pause, Qt::DirectConnection); fileCopy.connect(this, &FileOperation::operationResume, &fileCopy, &FileCopy::resume, Qt::DirectConnection); fileCopy.connect(this, &FileOperation::operationCancel, &fileCopy, &FileCopy::cancel, Qt::DirectConnection); if (m_is_pause) fileCopy.pause(); fileCopy.run(); if (err) { switch (err->code) { case G_IO_ERROR_INVALID_FILENAME: { QString newDestUri; if (makeFileNameValidForDestFS(m_current_src_uri, m_dest_dir_uri, &newDestUri)) { if (newDestUri != destName) { destName = newDestUri; node->setDestFileName(newDestUri); goto fallback_retry; } } break; } case G_IO_ERROR_CANCELLED: return; case G_IO_ERROR_EXISTS: char* destFileName = g_file_get_uri(destFile.get()->get()); if (NULL != destFileName) { m_conflict_files << destFileName; g_free(destFileName); } break; } FileOperationError except; auto errWrapperPtr = GErrorWrapper::wrapFrom(err); int handle_type = prehandle(err); except.errorType = ET_GIO; except.op = FileOpCopy; except.title = tr("File copy error"); except.srcUri = m_current_src_uri; except.errorCode = err->code; except.errorStr = err->message; except.destDirUri = m_current_dest_dir_uri; // if (err->code == G_IO_ERROR_PERMISSION_DENIED) { except.errorStr = tr("Cannot opening file, permission denied!"); } if (handle_type == Other) { if (G_IO_ERROR_EXISTS == err->code) { except.dlgType = ED_CONFLICT; Q_EMIT errored(except); auto typeData = except.respCode; qDebug()<<"get return"; handle_type = typeData; } else { except.dlgType = ED_WARNING; Q_EMIT errored(except); auto typeData = except.respCode; qDebug()<<"get return"; handle_type = typeData; } } //handle. switch (handle_type) { case IgnoreOne: { node->setState(FileNode::Unhandled); node->setErrorResponse(IgnoreOne); break; } case IgnoreAll: { node->setState(FileNode::Unhandled); node->setErrorResponse(IgnoreOne); m_prehandle_hash.insert(err->code, IgnoreOne); break; } case OverWriteOne: { FileCopy fileOverWriteOneCopy (node->uri(), destFileUri, (GFileCopyFlags)(m_default_copy_flag | G_FILE_COPY_OVERWRITE), getCancellable().get()->get(), GFileProgressCallback(progress_callback), this, nullptr); fileCopy.connect(this, &FileOperation::operationPause, &fileOverWriteOneCopy, &FileCopy::pause, Qt::DirectConnection); fileCopy.connect(this, &FileOperation::operationResume, &fileOverWriteOneCopy, &FileCopy::resume, Qt::DirectConnection); fileCopy.connect(this, &FileOperation::operationCancel, &fileOverWriteOneCopy, &FileCopy::cancel, Qt::DirectConnection); if (m_is_pause) fileOverWriteOneCopy.pause(); fileOverWriteOneCopy.run(); node->setState(FileNode::Handled); node->setErrorResponse(OverWriteOne); m_is_duplicated_copy = false; break; } case OverWriteAll: { FileCopy fileOverWriteOneCopy (node->uri(), destFileUri, (GFileCopyFlags)(m_default_copy_flag | G_FILE_COPY_OVERWRITE), getCancellable().get()->get(), GFileProgressCallback(progress_callback), this, nullptr); fileCopy.connect(this, &FileOperation::operationPause, &fileOverWriteOneCopy, &FileCopy::pause, Qt::DirectConnection); fileCopy.connect(this, &FileOperation::operationResume, &fileOverWriteOneCopy, &FileCopy::resume, Qt::DirectConnection); fileCopy.connect(this, &FileOperation::operationCancel, &fileOverWriteOneCopy, &FileCopy::cancel, Qt::DirectConnection); if (m_is_pause) fileOverWriteOneCopy.pause(); fileOverWriteOneCopy.run(); node->setState(FileNode::Handled); node->setErrorResponse(OverWriteOne); m_prehandle_hash.insert(err->code, OverWriteOne); m_is_duplicated_copy = false; break; } case BackupOne: { node->setState(FileNode::Handled); node->setErrorResponse(BackupOne); // use custom name QString name = ""; QStringList extendStr = node->destBaseName().split("."); if (extendStr.length() > 0) { extendStr.removeAt(0); } QString endStr = extendStr.join("."); if (except.respValue.contains("name")) { name = except.respValue["name"].toString(); if (endStr != "" && name.endsWith(endStr)) { node->setDestFileName(name); } else if ("" != endStr && "" != name) { node->setDestFileName(name + "." + endStr); } } while (FileUtils::isFileExsit(node->resolveDestFileUri(m_dest_dir_uri))) { handleDuplicate(node); } goto fallback_retry; } case BackupAll: { node->setState(FileNode::Handled); node->setErrorResponse(BackupOne); while (FileUtils::isFileExsit(node->resolveDestFileUri(m_dest_dir_uri))) { handleDuplicate(node); } m_prehandle_hash.insert(err->code, BackupOne); goto fallback_retry; } case Retry: { goto fallback_retry; } case Cancel: { node->setState(FileNode::Unhandled); cancel(); break; } default: break; } } else { node->setState(FileNode::Handled); } if (is_desktop_file) { QUrl url(node->destUri()); QRegExp regExp (QString("\\ -\\ %1\\(\\d+\\)(\\.[0-9a-zA-Z\\.]+|)$").arg(QObject::tr("duplicate"))); GDesktopAppInfo *desktop_info = g_desktop_app_info_new_from_filename(url.path().toUtf8().constData()); if (G_IS_DESKTOP_APP_INFO(desktop_info)) { GKeyFile *key_file = g_key_file_new(); g_key_file_load_from_file(key_file, url.path().toUtf8().constData(), G_KEY_FILE_KEEP_COMMENTS, nullptr); QString locale_name = QLocale::system().name(); QString ext; QString local_generic_name_key = QString("Name[%1]").arg(locale_name); g_autofree char* nameStr = nullptr; if (node->destBaseName().contains(regExp)) { ext = regExp.cap(0).replace(".desktop", ""); } if (g_key_file_has_key(key_file, G_KEY_FILE_DESKTOP_GROUP, local_generic_name_key.toUtf8().constData(), nullptr)) { nameStr = g_key_file_get_value(key_file, G_KEY_FILE_DESKTOP_GROUP, local_generic_name_key.toUtf8().constData(), nullptr); g_key_file_set_value(key_file, G_KEY_FILE_DESKTOP_GROUP, local_generic_name_key.toUtf8().constData(), QString(nameStr + ext).toUtf8().constData()); } else { nameStr = g_key_file_get_value(key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, nullptr); g_key_file_set_value(key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, QString(nameStr + ext).toUtf8().constData()); } qDebug() << "set desktop name:" << nameStr + ext << " -- " << url.path(); GError* error = NULL; g_key_file_save_to_file(key_file, url.path().toUtf8().constData(), &error); if (error) { qWarning() << "save file error:" << error->code << " -- " << error->message; g_error_free(error); error = nullptr; } g_key_file_free(key_file); g_object_unref(desktop_info); if (nullptr != srcInfo) { g_autoptr(GFile) destFile = g_file_new_for_uri(node->destUri().toUtf8().constData()); if (destFile) { g_file_set_attribute_uint32(destFile, G_FILE_ATTRIBUTE_UNIX_MODE, g_file_info_get_attribute_uint32(srcInfo, G_FILE_ATTRIBUTE_UNIX_MODE), G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); } } } else { qDebug() << "desktop file:" << node->destUri() << " is wrong"; } } m_current_offset += node->size(); fileSync(srcUri, destFileUri); Q_EMIT operationProgressedOne(node->uri(), node->destUri(), node->size()); } destFile.reset(); } void FileCopyOperation::rollbackNodeRecursively(FileNode *node) { switch (node->state()) { case FileNode::Handling: case FileNode::Handled: { if (node->isFolder()) { auto children = node->children(); for (auto child : *children) { rollbackNodeRecursively(child); } GFile *dest_file = g_file_new_for_uri(node->destUri().toUtf8().constData()); //FIXME: there's a certain probability of failure to delete the folder without //any problem happended. because somehow an empty file will created in the folder. //i don't know why, but it is obvious that i have to delete them at first. bool is_folder_deleted = g_file_delete(dest_file, nullptr, nullptr); if (!is_folder_deleted) { FileEnumerator e; e.setEnumerateDirectory(node->destUri()); e.enumerateSync(); for (auto folder_child : *node->children()) { if (!folder_child->destUri().isEmpty()) { GFile *tmp_file = g_file_new_for_uri(folder_child->destUri().toUtf8().constData()); g_file_delete(tmp_file, nullptr, nullptr); g_object_unref(tmp_file); } g_file_delete(dest_file, nullptr, nullptr); } } g_object_unref(dest_file); } else { if (!m_conflict_files.contains(node->destUri())) { GFile *dest_file = g_file_new_for_uri(node->destUri().toUtf8().constData()); g_file_delete(dest_file, nullptr, nullptr); g_object_unref(dest_file); } } operationRollbackedOne(node->destUri(), node->uri()); break; } default: { //make sure all nodes were rollbacked. if (node->isFolder()) { auto children = node->children(); for (auto child : *children) { rollbackNodeRecursively(child); } } break; } } } void FileCopyOperation::run() { if (isCancelled()) return; Q_EMIT operationStarted(); Q_EMIT operationRequestShowWizard(); goffset *total_size = new goffset(0); QList nodes; for (auto uri : m_source_uris) { qDebug() << "copy uri:" << uri; QString szTempUri = uri; if(szTempUri.startsWith("filesafe:///") && szTempUri.remove("filesafe:///").indexOf("/") == -1) { continue; } FileNode *node = new FileNode(uri, nullptr, m_reporter); node->findChildrenRecursively(); node->computeTotalSize(total_size); nodes << node; } Q_EMIT operationPrepared(); m_total_szie = *total_size; delete total_size; for (auto node : nodes) { copyRecursively(node); } Q_EMIT operationProgressed(); if (isCancelled()) { Q_EMIT operationStartRollbacked(); for (auto file : nodes) { qDebug()<uri(); if (isCancelled()) { rollbackNodeRecursively(file); } } } setHasError(false); for (auto node : nodes) { if (!isCancelled()) m_info->m_node_map.insert(node->uri(), node->destUri()); delete node; } m_info->m_dest_uris = m_info->m_node_map.values(); nodes.clear(); Q_EMIT operationFinished(); } void FileCopyOperation::cancel() { if (m_reporter) { m_reporter->cancel(); } ClipboardUtils::popLastTargetDirectoryUri(m_dest_dir_uri); FileOperation::cancel(); } peony/libpeony-qt/file-operation/file-trash-operation.cpp0000644000175000017500000002415114205115226022551 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-trash-operation.h" #include "file-operation-manager.h" #include "file-enumerator.h" #include #include #include #include #define TRASH_TIME "trash-time" using namespace Peony; FileTrashOperation::FileTrashOperation(QStringList srcUris, QObject *parent) : FileOperation (parent) { m_src_uris = srcUris; m_info = std::make_shared(srcUris, "trash:///", FileOperationInfo::Trash); m_total_count = m_src_uris.length (); } void FileTrashOperation::run() { Q_EMIT operationStarted(); Peony::ExceptionResponse response = Invalid; for (auto src : m_src_uris) { Q_EMIT operationPreparedOne (src, 1); } Q_EMIT operationPrepared (); for (auto src : m_src_uris) { if (isCancelled()) break; // cache file info m_src_infos<isDir() && (!fileInfo->canRead() || !fileInfo->canWrite())) { // FileOperationError except; // except.srcUri = src; // except.destDirUri = tr("trash:///"); // except.isCritical = true; // except.op = FileOpTrash; // except.title = tr("Trash file error"); // except.errorCode = G_IO_ERROR_FAILED; // except.errorStr = QString(tr("The user does not have read and write rights to the file '%1' and cannot delete it to the Recycle Bin.").arg(fileInfo->displayName())); // except.errorType = ET_GIO; // except.dlgType = ED_WARNING; // Q_EMIT errored(except); // if (Cancel == except.respCode) { // cancel(); // } // continue; // } // } g_file_trash(srcFile.get()->get(), getCancellable().get()->get(), &err); if (err) { if (response == IgnoreAll) { g_error_free(err); continue; } // ref the error, released after error handling. auto refPtr = GErrorWrapper::wrapFrom(err); FileOperationError except; except.srcUri = src; except.destDirUri = tr("trash:///"); except.isCritical = true; except.op = FileOpTrash; except.title = tr("Trash file error"); except.errorCode = err->code; except.errorStr = err->message; except.errorType = ET_GIO; if (G_IO_ERROR_EXISTS == err->code) { except.dlgType = ED_CONFLICT; Q_EMIT errored(except); auto responseType = except.respCode; auto responseData = responseType; switch (responseData) { case Retry: goto retry; case Cancel: cancel(); break; case IgnoreAll: response = IgnoreAll; break; default: break; } } else { if (err->code == G_IO_ERROR_NOT_SUPPORTED) { except.dlgType = ED_NOT_SUPPORTED; auto fileName = g_file_get_basename(srcFile.get()->get()); except.errorStr = tr("Can not trash this file, would you like to delete it permanently?"); if (fileName) { g_free(fileName); } } else if (err->code == G_IO_ERROR_FILENAME_TOO_LONG) { char *orig_path = g_file_get_path(srcFile.get()->get()); char *basename = g_file_get_basename(srcFile.get()->get()); QString trashDir = QString(g_get_user_data_dir()) + "/Trash/files"; GFile *trash = g_file_new_for_path(trashDir.toUtf8().constData()); GFile *dest_file = g_file_resolve_relative_path(trash, basename); g_object_unref(trash); g_file_move(srcFile.get()->get(), dest_file, G_FILE_COPY_NOFOLLOW_SYMLINKS, nullptr, nullptr, nullptr, nullptr); g_file_set_attribute_string(dest_file, "metadata::orig-path", orig_path, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); g_object_unref(dest_file); if (orig_path) { g_free(orig_path); } if (basename) { g_free(basename); } response = IgnoreOne; } else { except.dlgType = ED_WARNING; } if (response == Invalid) Q_EMIT errored(except); auto responseType = except.respCode; auto responseData = responseType; if (response != Invalid) responseData = response; switch (responseData) { case Retry: goto retry; case OverWriteOne: { GError *err1 = nullptr; g_file_delete(srcFile.get()->get(), getCancellable().get()->get(), &err1); if (err1) { g_error_free(err1); } break; } case OverWriteAll: { response = OverWriteOne; goto retry; } case Cancel: cancel(); break; case IgnoreAll: response = IgnoreAll; break; case ForceAll: response = ForceAll; case Force: forceDelete(src); break; default: break; } } } else { // fileinfo关联删除时间 quint64 time = QDateTime::currentSecsSinceEpoch(); auto info = FileInfo::fromUri(src); info.get()->setProperty(TRASH_TIME, time); } FileProgressCallback("trash:///", src, "", ++m_current_count, m_total_count); } // judge if the operation should sync. // bool needSync = false; // GFile *src_first_file = g_file_new_for_uri(m_src_uris.first().toUtf8().constData()); // GMount *src_first_mount = g_file_find_enclosing_mount(src_first_file, nullptr, nullptr); // if (src_first_mount) { // needSync = g_mount_can_unmount(src_first_mount); // g_object_unref(src_first_mount); // } else { // // maybe a vfs file. // needSync = true; // } // g_object_unref(src_first_file); // if (needSync) { // operationStartSnyc(); // QProcess p; // p.start("sync"); // p.waitForFinished(-1); // } // fix dest uris in trash in FileOperationInfo // fileinfo关联被删除的时间,然后枚举trash中displayname相同的文件,对比时间差最近的文件作为待恢复的文件 QStringList destUris; FileEnumerator e; e.setEnumerateDirectory("trash:///"); e.enumerateSync(); for (auto trashFileInfo : e.getChildren()) { FileInfoJob trashInfoJob(trashFileInfo); trashInfoJob.querySync(); for (auto info : m_src_infos) { if (info.get()->displayName() == trashFileInfo.get()->displayName()) { // allow 5 seconds deviation if (qAbs(trashFileInfo.get()->getDeletionDateUInt64() - info.get()->property(TRASH_TIME).toUInt()) < 5) { destUris<uri(); } } } } if (!destUris.isEmpty()) { m_info.get()->m_dest_uris.clear(); m_info.get()->m_dest_uris<findChildrenRecursively(); deleteRecursively(node); if (node) delete node; } void FileTrashOperation::deleteRecursively(FileNode *node) { if (isCancelled()) return; g_autoptr(GFile) file = g_file_new_for_uri(FileUtils::urlEncode(node->uri()).toUtf8().constData()); if (node->isFolder()) { for (auto child : *(node->children())) { deleteRecursively(child); } GError *err = nullptr; g_file_delete(file, getCancellable().get()->get(), &err); if (err) { qDebug() << "force delete folder:" << node->uri() << " error: " << err->message; g_error_free(err); } } else { GError *err = nullptr; g_file_delete(file, getCancellable().get()->get(), &err); if (err) { qDebug() << "force delete file:" << node->uri() << " error: " << err->message; g_error_free(err); } } } void FileTrashOperation::setErrorMessage(GError** err) { } peony/libpeony-qt/file-operation/file-count-operation.cpp0000644000175000017500000000502514205101223022547 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-count-operation.h" #include "file-node-reporter.h" #include "file-node.h" using namespace Peony; FileCountOperation::FileCountOperation(const QStringList &uris, bool countRoot, QObject *parent) : FileOperation (parent) { m_count_root = countRoot; m_reporter = new FileNodeReporter(this); //connect(m_reporter, &FileNodeReporter::nodeFound, this, &FileOperation::operationPreparedOne); connect(m_reporter, &FileNodeReporter::nodeFound, [=](const QString &uri, quint64 size) { m_file_count++; if (uri.contains("/.")) { m_hidden_file_count++; } m_total_size += size; Q_EMIT this->operationPreparedOne(uri, size); }); m_uris = uris; } FileCountOperation::~FileCountOperation() { } void FileCountOperation::cancel() { FileOperation::cancel(); m_reporter->cancel(); } void FileCountOperation::run() { Q_EMIT operationStarted(); if (m_uris.isEmpty()) Q_EMIT operationFinished(); QList nodes; for (auto uri : m_uris) { auto node = new FileNode(FileUtils::urlEncode(uri), nullptr, m_reporter); node->findChildrenRecursively(); nodes<isCancelled()) { if (!m_count_root) { for (auto node : nodes) { m_file_count--; if (node->baseName().startsWith(".")) { m_hidden_file_count--; } //m_total_size -= node->size(); } } Q_EMIT countDone(m_file_count, m_hidden_file_count, m_total_size); } qDebug()<. * * Authors: Jing Ding * */ #include "file-operation-error-dialog-base.h" #include #include #include #include #include #include static QPixmap drawSymbolicColoredPixmap (const QPixmap& source); Peony::FileOperationErrorDialogBase::FileOperationErrorDialogBase(QDialog *parent) : QDialog(parent) { setMouseTracking(true); setContentsMargins(0, 0, 0, 0); setWindowFlags(Qt::FramelessWindowHint); connect(this, &FileOperationErrorDialogBase::cancel, this, [=](){ done(QDialog::Rejected); }); } Peony::FileOperationErrorDialogBase::~FileOperationErrorDialogBase() { } void Peony::FileOperationErrorDialogBase::setHeaderIcon(QString icon) { m_header_icon = icon; } void Peony::FileOperationErrorDialogBase::paintEvent(QPaintEvent *) { QPainter painter(this); painter.save(); QPushButton btn; // paint icon if (!m_header_icon.isEmpty()) { } painter.drawPixmap(m_margin_lr, (m_header_height - m_header_icon_size) / 2, m_header_icon_size, m_header_icon_size, QIcon::fromTheme(m_header_icon.isEmpty()?"dialog-error":m_header_icon).pixmap(QSize(m_header_icon_size, m_header_icon_size))); // paint title QRect textArea (m_margin_lr + m_header_icon_size + m_btn_margin, 0, width() - m_margin_lr * 2 - 2 * m_btn_size, m_header_height); QFont font = painter.font(); font.setPixelSize(14); painter.setFont(font); painter.setBrush(QBrush(btn.palette().color(QPalette::Highlight).lighter(150))); if (nullptr != m_error && nullptr != m_error->title) { painter.drawText(textArea, Qt::AlignLeft | Qt::AlignVCenter, m_error->title); } // paint minilize button /*QPen pen(QColor(192,192,192), 1); pen.setStyle(Qt::SolidLine); painter.setBrush(Qt::NoBrush); painter.setPen(pen); painter.drawPixmap(width() - m_margin_lr * 2 - m_btn_size * 2, m_margin_tp, m_btn_size, m_btn_size, drawSymbolicColoredPixmap(QIcon::fromTheme("window-minimize-symbolic").pixmap(m_btn_size, m_btn_size))); */ // paint close button QPen pen; pen.setStyle(Qt::SolidLine); painter.setBrush(Qt::NoBrush); painter.setPen(pen); painter.drawPixmap(width() - m_margin_lr - m_btn_size, m_margin_tp, m_btn_size, m_btn_size, drawSymbolicColoredPixmap(QIcon::fromTheme("window-close-symbolic").pixmap(m_btn_size, m_btn_size))); painter.restore(); } void Peony::FileOperationErrorDialogBase::mouseMoveEvent(QMouseEvent *event) { // minilize button QPoint pos = event->pos(); /*if ((pos.x() >= width() - m_margin_lr * 2 - m_btn_size * 2) && (pos.x() <= width() - m_margin_lr * 2 - m_btn_size) && (pos.y() >= m_margin_tp) && (pos.y() <= m_margin_tp + m_btn_size)) { setCursor(Qt::PointingHandCursor); } else */if ((pos.x() >= width() - m_margin_lr - m_btn_size) && (pos.x() <= width() - m_margin_lr) && (pos.y() >= m_margin_tp) && (pos.y() <= m_margin_tp + m_btn_size)) { setCursor(Qt::PointingHandCursor); } else { setCursor(Qt::ArrowCursor); QWidget::mouseMoveEvent(event); } } void Peony::FileOperationErrorDialogBase::mousePressEvent(QMouseEvent *event) { // minilize button QPoint pos = event->pos(); /*if ((pos.x() >= width() - m_margin_lr * 2 - m_btn_size * 2) && (pos.x() <= width() - m_margin_lr * 2 - m_btn_size) && (pos.y() >= m_margin_tp) && (pos.y() <= m_margin_tp + m_btn_size)) { showMinimized(); } else */if ((pos.x() >= width() - m_margin_lr - m_btn_size) && (pos.x() <= width() - m_margin_lr) && (pos.y() >= m_margin_tp) && (pos.y() <= m_margin_tp + m_btn_size)) { Q_EMIT cancel(); } QWidget::mouseReleaseEvent(event); } static QPixmap drawSymbolicColoredPixmap (const QPixmap& source) { // 18, 32, 69 QPushButton m_btn; QColor baseColor = m_btn.palette().color(QPalette::Text).light(150); QImage img = source.toImage(); for (int x = 0; x < img.width(); ++x) { for (int y = 0; y < img.height(); ++y) { auto color = img.pixelColor(x, y); color.setRed(baseColor.red()); color.setGreen(baseColor.green()); color.setBlue(baseColor.blue()); img.setPixelColor(x, y, color); } } return QPixmap::fromImage(img); } peony/libpeony-qt/file-operation/file-delete-operation.h0000644000175000017500000000361714205101223022333 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILEDELETEOPERATION_H #define FILEDELETEOPERATION_H #include "file-operation.h" #include "peony-core_global.h" namespace Peony { class FileNode; class FileNodeReporter; class PEONYCORESHARED_EXPORT FileDeleteOperation : public FileOperation { Q_OBJECT public: explicit FileDeleteOperation(QStringList sourceUris, QObject *parent = nullptr); ~FileDeleteOperation() override; std::shared_ptr getOperationInfo() override; void deleteRecursively(FileNode *node); void run() override; void cancel() override; private: QStringList m_source_uris; int m_current_count = 0; int m_total_count = 0; QString m_current_src_uri = nullptr; goffset m_current_offset = 0; goffset m_total_szie = 0; FileNodeReporter *m_reporter = nullptr; /*! * \brief m_prehandle_hash * \details * Once a move operation get into error, this class might cache the specific response * for next prehandleing. */ QHash m_prehandle_hash; std::shared_ptr m_info = nullptr; }; } #endif // FILEDELETEOPERATION_H peony/libpeony-qt/file-operation/file-operation-progress-bar.cpp0000644000175000017500000006513614205115226024046 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Jing Ding * */ #include "file-operation-progress-bar.h" #include #include #include #include #include #include #include #include "file-utils.h" #include QPushButton* btn; static QPixmap drawSymbolicColoredPixmap (const QPixmap&); FileOperationProgressBar *FileOperationProgressBar::instance = nullptr; FileOperationProgressBar *FileOperationProgressBar::getInstance() { if (nullptr == FileOperationProgressBar::instance) { instance = new FileOperationProgressBar; } return instance; } void FileOperationProgressBar::removeAllProgressbar() { if (nullptr != m_main_progressbar && nullptr != m_current_main) { m_main_progressbar->disconnect(m_current_main, &ProgressBar::sendValue, 0, 0); m_current_main = nullptr; } for (auto pg = m_widget_list->constBegin(); pg != m_widget_list->constEnd(); ++pg) { if (pg.value()->m_current_size > 0) continue; if (nullptr != pg.value()) pg.value()->deleteLater(); if (nullptr != pg.key()) delete pg.key(); } m_widget_list->clear(); m_list_widget->clear(); m_progress_list->clear(); m_progress_size = 0; hide(); } ProgressBar *FileOperationProgressBar::addFileOperation() { ProgressBar* proc = new ProgressBar; QListWidgetItem* li = new QListWidgetItem(m_list_widget); m_list_widget->addItem(li); m_list_widget->setItemWidget(li, proc); (*m_progress_list)[proc] = li; (*m_widget_list)[li] = proc; li->setSizeHint(QSize(m_list_widget->width(), m_progress_item_height)); li->setFlags(Qt::NoItemFlags); proc->connect(proc, &ProgressBar::finished, this, &FileOperationProgressBar::removeFileOperation); ++m_progress_size; if (nullptr == m_current_main) { mainProgressChange(li); } setWindowState(windowState() & ~Qt::WindowMinimized | Qt::WindowActive); showMore(); return proc; } void FileOperationProgressBar::showProgress(ProgressBar &progress) { if (m_progress_size > 0) { setAttribute(Qt::WA_TranslucentBackground, true); progress.show(); show(); QTimer::singleShot(300, [=] () { setAttribute(Qt::WA_TranslucentBackground, false); }); } } void FileOperationProgressBar::removeFileOperation(ProgressBar *progress) { progress->hide(); QListWidgetItem* li = (*m_progress_list)[progress]; m_list_widget->removeItemWidget(li); m_progress_list->remove(progress); m_widget_list->remove(li); --m_progress_size; // check main progress if (m_current_main == progress) { // check other progress if (m_progress_size > 0) { QListWidgetItem* pg = m_progress_list->first(); m_current_main = (*m_widget_list)[pg]; mainProgressChange(pg); } } // free progress progress->deleteLater(); delete li; if (m_progress_size <= 0) { m_progress_size = 0; m_current_main = nullptr; hide(); } showMore(); } FileOperationProgressBar::FileOperationProgressBar(QWidget *parent) : QWidget(parent) { m_current_main = nullptr; setWindowFlags(Qt::FramelessWindowHint); setContentsMargins(0, 0, 0, 0); setProperty("useCustomShadow", true); setProperty("customShadowDarkness", 0.5); setProperty("customShadowWidth", 30); setProperty("customShadowRadius", QVector4D(1, 1, 1, 1)); setProperty("customShadowMargins", QVector4D(30, 30, 30, 30)); btn = new QPushButton(nullptr); m_main_layout = new QVBoxLayout(this); m_main_layout->setContentsMargins(0, 0, 0, 0); m_main_progressbar = new MainProgressBar(nullptr); m_other_progressbar = new OtherButton(nullptr); m_list_widget = new QListWidget(nullptr); m_list_widget->setFrameShape(QListWidget::NoFrame); m_list_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); m_list_widget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_main_layout->addWidget(m_main_progressbar); m_main_layout->addWidget(m_other_progressbar); m_main_layout->addWidget(m_list_widget); m_progress_list = new QMap; m_widget_list = new QMap; showWidgetList(false); connect(m_main_progressbar, &MainProgressBar::minimized, this, [=]() { this->showMinimized(); }); connect(m_main_progressbar, &MainProgressBar::closeWindow, this, [=](){ for (auto pg = m_widget_list->constBegin(); pg != m_widget_list->constEnd(); ++pg) { Q_EMIT pg.value()->cancelled(); } Q_EMIT canceled(); }); connect(m_other_progressbar, &OtherButton::clicked, this, &FileOperationProgressBar::showWidgetList); connect(m_list_widget, &QListWidget::itemClicked, this, &FileOperationProgressBar::mainProgressChange); connect(this, &FileOperationProgressBar::pause, this, [=] () { m_main_progressbar->setPause(); for (auto pb : m_progress_list->keys()) { pb->setPause(); } }); showMore(); } FileOperationProgressBar::~FileOperationProgressBar() { delete btn; } void FileOperationProgressBar::showMore() { if (m_progress_size > 1) { m_other_progressbar->show(); if (m_progress_size > 1 && m_progress_size <= m_show_items) { m_list_widget->setFixedHeight(m_progress_size * m_progress_item_height); } else if (m_progress_size > m_show_items) { m_list_widget->setFixedHeight(m_show_items * m_progress_item_height); } if (m_show_more) { m_list_widget->show(); m_other_progressbar->show(); setFixedSize(m_main_progressbar->width(), m_main_progressbar->height() + m_other_progressbar->height() + m_list_widget->height()); } else { m_list_widget->hide(); setFixedSize(m_main_progressbar->width(), m_main_progressbar->height() + m_other_progressbar->height()); } } else { m_list_widget->hide(); m_other_progressbar->hide(); setFixedSize(m_main_progressbar->width(), m_main_progressbar->height()); } update(); } void FileOperationProgressBar::mouseMoveEvent(QMouseEvent *event) { if (m_is_press) { move(event->globalPos() - m_position); event->accept(); } } void FileOperationProgressBar::mousePressEvent(QMouseEvent *event) { if (Qt::LeftButton == event->button()) { m_is_press = true; m_position = event->globalPos() - frameGeometry().topLeft(); } } void FileOperationProgressBar::mouseReleaseEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { m_is_press = false; } } void FileOperationProgressBar::showWidgetList(bool show) { m_show_more = show; if (show) { m_list_widget->show(); } else { m_list_widget->hide(); } showMore(); } void FileOperationProgressBar::mainProgressChange(QListWidgetItem *item) { if (nullptr != m_main_progressbar && nullptr != m_current_main) { m_main_progressbar->disconnect(m_current_main, &ProgressBar::sendValue, 0, 0); m_current_main->disconnect(m_main_progressbar, &MainProgressBar::start, 0, 0); m_current_main->disconnect(m_main_progressbar, &MainProgressBar::pause, 0, 0); } ProgressBar* pb = (*m_widget_list)[item]; m_current_main = pb; m_main_progressbar->initPrarm(); if (m_current_main->getStatus()) { m_main_progressbar->cancelld(); } m_main_progressbar->setFileName(pb->getFileName()); m_main_progressbar->setProgress(pb->getProgress()); m_main_progressbar->setFileIcon(m_current_main->getIcon()); m_main_progressbar->connect(m_current_main, &ProgressBar::cancelled, m_main_progressbar, &MainProgressBar::cancelld); m_main_progressbar->connect(m_current_main, &ProgressBar::sendValue, m_main_progressbar, &MainProgressBar::updateValue); if (m_current_main->isPause()) { m_main_progressbar->setPause(); } else { m_main_progressbar->setResume(); } m_main_progressbar->setIsSync(pb->m_sync); m_current_main->connect(m_main_progressbar, &MainProgressBar::pause, this, [=] () { m_current_main->setPause(); m_main_progressbar->setPause(); }); m_current_main->connect(m_main_progressbar, &MainProgressBar::start, this, [=] () { m_current_main->setResume(); m_main_progressbar->setResume(); }); update(); } void FileOperationProgressBar::showDelay(int msec) { QTimer::singleShot(msec, this, [=] () { if (m_list_widget->count() > 0 && !m_error) { show(); } }); } MainProgressBar::MainProgressBar(QWidget *parent) : QWidget(parent) { setWindowFlags(Qt::FramelessWindowHint); setMouseTracking(true); m_title = tr("File operation"); setFixedSize(m_fix_width, m_fix_height); } void MainProgressBar::initPrarm() { m_sync = false; m_show = false; m_pause = false; m_stopping = false; m_current_value = 0.0; m_file_name = tr("starting ..."); } void MainProgressBar::setFileIcon(QIcon& icon) { m_icon = icon; } void MainProgressBar::setTitle(QString title) { m_title = title; } void MainProgressBar::setPause() { m_pause = true; update(); } void MainProgressBar::setResume() { m_pause = false; update(); } void MainProgressBar::setIsSync(bool sync) { m_sync = sync; } void MainProgressBar::setProgress(float val) { m_current_value = val; } void MainProgressBar::setFileName(QString name) { m_file_name = name; } void MainProgressBar::paintEvent(QPaintEvent *event) { QPainter painter (this); painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); paintProgress (painter); paintHeader (painter); paintFoot (painter); paintContent (painter); Q_UNUSED(event); } void MainProgressBar::mouseMoveEvent(QMouseEvent *event) { // minilize button QPoint pos = event->pos(); if ((pos.x() >= m_minilize_button_x_l) && (pos.x() <= m_minilize_button_x_r) && (pos.y() >= m_minilize_button_y_t) && (pos.y() <= m_minilize_button_y_b)) { setCursor(Qt::PointingHandCursor); } else if ((pos.x() >= m_close_button_x_l) && (pos.x() <= m_close_button_x_r) && (pos.y() >= m_close_button_y_t) && (pos.y() <= m_close_button_y_b)) { setCursor(Qt::PointingHandCursor); } else if ((pos.x() >= m_progress_pause_x) && (pos.x() <= m_progress_pause_x_r) && (pos.y() >= m_progress_pause_y) && (pos.y() <= m_progress_pause_y_b)) { setCursor(Qt::PointingHandCursor); } else { setCursor(Qt::ArrowCursor); QWidget::mouseMoveEvent(event); } } void MainProgressBar::mouseReleaseEvent(QMouseEvent *event) { // minilize button QPoint pos = event->pos(); if ((pos.x() >= m_minilize_button_x_l) && (pos.x() <= m_minilize_button_x_r) && (pos.y() >= m_minilize_button_y_t) && (pos.y() <= m_minilize_button_y_b)) { Q_EMIT minimized(); } else if ((pos.x() >= m_close_button_x_l) && (pos.x() <= m_close_button_x_r) && (pos.y() >= m_close_button_y_t) && (pos.y() <= m_close_button_y_b)) { QMessageBox msgBox(QMessageBox::Warning, tr("cancel all file operations"), tr("Are you sure want to cancel all file operations"), QMessageBox::Ok | QMessageBox::Cancel); msgBox.button(QMessageBox::Ok)->setText(tr("OK")); msgBox.button(QMessageBox::Cancel)->setText(tr("Cancel")); if (QMessageBox::Ok == msgBox.exec()) { Q_EMIT closeWindow(); } } else if ((pos.x() >= m_progress_pause_x) && (pos.x() <= m_progress_pause_x_r) && (pos.y() >= m_progress_pause_y) && (pos.y() <= m_progress_pause_y_b)) { if (m_pause) { Q_EMIT start(); } else { Q_EMIT pause(); } } QWidget::mouseReleaseEvent(event); } void MainProgressBar::paintFoot(QPainter &painter) { painter.save(); double value = m_current_value * m_fix_width; QPushButton btn; // QLinearGradient progressBarBgGradient (QPointF(0, 0), QPointF(0, height())); // progressBarBgGradient.setColorAt(0.0, QColor(75,0,130)); // progressBarBgGradient.setColorAt(1.0, QColor(75,0,130)); // painter.setBrush(progressBarBgGradient); painter.setPen(Qt::NoPen); painter.setBrush(QBrush(btn.palette().color(QPalette::Button))); painter.drawRoundedRect(0, m_foot_progress_back_y, m_fix_width, m_foot_margin, 1, 1); painter.setBrush(QBrush(btn.palette().color(QPalette::Highlight))); painter.drawRoundedRect(0, m_foot_progress_back_y, value, m_foot_margin, 1, 1); painter.restore(); } void MainProgressBar::paintHeader(QPainter &painter) { painter.save(); // paint title QRect textArea (m_text_area_x, 0, m_title_width, m_header_height); QFont font = painter.font(); font.setPixelSize(14); painter.setFont(font); painter.drawText(textArea, Qt::AlignVCenter | Qt::AlignHCenter, m_title); // paint minilize button painter.drawPixmap(m_minilize_button_x_l, m_btn_margin_top, m_btn_size, m_btn_size, drawSymbolicColoredPixmap(QIcon::fromTheme("window-minimize-symbolic").pixmap(m_btn_size, m_btn_size))); // paint close button painter.drawPixmap(m_close_button_x_l, m_btn_margin_top, m_btn_size, m_btn_size, drawSymbolicColoredPixmap(QIcon::fromTheme("window-close-symbolic").pixmap(m_btn_size, m_btn_size))); painter.restore(); } void MainProgressBar::paintContent(QPainter &painter) { painter.save(); // paint icon if (m_icon.isNull()) { painter.drawPixmap(m_icon_margin, m_icon_area_y, m_icon_size, m_icon_size, QIcon::fromTheme("text").pixmap(m_icon_size, m_icon_size)); } else { painter.drawPixmap(m_icon_margin, m_icon_area_y, m_icon_size, m_icon_size, m_icon.pixmap(m_icon_size, m_icon_size)); } // paint file name QFont font = painter.font(); font.setPixelSize(14); painter.setFont(font); if (m_stopping) { painter.drawText(m_file_name_x, m_file_name_y, m_file_name_w, m_file_name_height, Qt::AlignLeft | Qt::AlignVCenter, tr("canceling ...")); } else { if (m_sync) { 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, QIcon::fromTheme("media-playback-pause-symbolic").pixmap(m_pause_btn_height, m_pause_btn_height)); } else { painter.drawText(m_file_name_x, m_file_name_y, m_file_name_w, m_file_name_height, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap | Qt::TextWrapAnywhere, m_file_name); if (m_pause) { painter.drawPixmap(m_progress_pause_x, m_progress_pause_y, QIcon::fromTheme("media-playback-start-symbolic").pixmap(m_pause_btn_height, m_pause_btn_height)); } else { painter.drawPixmap(m_progress_pause_x, m_progress_pause_y, QIcon::fromTheme("media-playback-pause-symbolic").pixmap(m_pause_btn_height, m_pause_btn_height)); } } } // paint percentage font.setPixelSize(12); painter.setFont(font); painter.setBrush(QBrush(btn->palette().color(QPalette::Highlight))); painter.drawText(m_percent_x, m_percent_y, m_percent_height, m_percent_height, Qt::AlignRight | Qt::AlignBottom, QString(" %1 %").arg(QString::number(m_current_value * 100, 'f', 1))); painter.restore(); } void MainProgressBar::paintProgress(QPainter &painter) { painter.save(); double value = m_current_value * m_fix_width; painter.setPen(Qt::NoPen); painter.setBrush(QBrush(btn->palette().color(QPalette::Highlight).lighter(150))); if (value < 1) { painter.drawRoundedRect(0, 0, value, m_fix_height, 1, 1); } painter.restore(); } void MainProgressBar::cancelld() { m_stopping = true; update(); } void MainProgressBar::updateValue(QString& name, QIcon& icon, double value) { if (value >= 0 && value < 1) { m_current_value = value; } m_file_name = Peony::FileUtils::urlDecode(name); m_icon = icon; update(); } OtherButton::OtherButton(QWidget *parent) : QWidget(parent) { setWindowFlags(Qt::FramelessWindowHint); setMouseTracking(true); setMinimumWidth(180); setFixedHeight(m_button_heigth); setContentsMargins(0, 0, 0, 0); } void OtherButton::paintEvent(QPaintEvent *event) { double x = 0; double y = 0; QPainter painter(this); painter.save(); QPen pen; pen.setStyle(Qt::SolidLine); painter.setPen(pen); // paint icon x = width() / 2 - m_icon_size - m_icon_margin -20; y = (height() - m_icon_size) / 2; QRect iconArea (x, y, m_icon_size, m_icon_size); if (m_show) { painter.drawPixmap(iconArea, drawSymbolicColoredPixmap(QIcon::fromTheme("kylin-fold").pixmap(m_icon_size, m_icon_size))); } else { painter.drawPixmap(iconArea, drawSymbolicColoredPixmap(QIcon::fromTheme("kylin-open").pixmap(m_icon_size, m_icon_size))); } // paint text x = x + m_icon_size + 10; QRect textArea (x, 0, m_text_length, m_button_heigth); QFont font = painter.font(); font.setPixelSize(10); painter.setFont(font); pen.setBrush(QBrush(btn->palette().color(QPalette::WindowText))); painter.setPen(pen); painter.drawText(textArea, Qt::AlignLeft | Qt::AlignVCenter, m_text); painter.restore(); Q_UNUSED(event); } void OtherButton::mousePressEvent(QMouseEvent *event) { if (!m_is_press) { m_is_press = true; } Q_UNUSED(event); } void OtherButton::mouseReleaseEvent(QMouseEvent *event) { if (m_is_press) { m_show = !m_show; Q_EMIT clicked(m_show); } m_is_press = false; Q_UNUSED(event); } ProgressBar::ProgressBar(QWidget *parent) : QWidget(parent) { setContentsMargins(0, 0, 0, 0); setFixedHeight(m_fix_height); setMouseTracking(true); m_is_stopping = false; setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); m_dest_uri = tr("starting ..."); connect(this, &ProgressBar::cancelled, this, &ProgressBar::onCancelled); connect(this, &ProgressBar::destroyed, this, [=] () {m_has_finished = true;}); } void ProgressBar::setIcon(const QString& icon) { if (nullptr != icon && "" != icon) { m_icon = QIcon::fromTheme(icon); } else { m_icon = QIcon::fromTheme("text"); } } QIcon& ProgressBar::getIcon() { return m_icon; } bool ProgressBar::getStatus() { return m_is_stopping; } float ProgressBar::getProgress() { return m_current_value; } float ProgressBar::getTotalSize() { return m_total_size; } QString ProgressBar::getFileName() { return m_dest_uri; } bool ProgressBar::isPause() { return m_pause; } void ProgressBar::setPause() { m_pause = true; Q_EMIT pause(); update(); } void ProgressBar::setResume() { m_pause = false; Q_EMIT resume(); update(); } ProgressBar::~ProgressBar() { } void ProgressBar::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); painter.save(); // paint icon if (m_icon.isNull()) { painter.drawPixmap(m_icon_x, m_icon_y, m_icon_size, m_icon_size, QIcon::fromTheme("text").pixmap(m_icon_size, m_icon_size)); } else { painter.drawPixmap(m_icon_x, m_icon_y, m_icon_size, m_icon_size, m_icon.pixmap(m_icon_size, m_icon_size)); } // paint text QPen pen; pen.setBrush(QBrush(btn->palette().color(QPalette::WindowText))); pen.setStyle(Qt::SolidLine); painter.setPen(pen); QFont font = painter.font(); font.setPixelSize(12); painter.setFont(font); if (m_is_stopping) { painter.drawText(m_text_x, m_text_y, m_text_w, m_text_height, Qt::AlignLeft | Qt::AlignVCenter, tr("canceling ...")); } else if (m_sync) { painter.drawText(m_text_x, m_text_y, m_text_w, m_text_height, Qt::AlignLeft | Qt::AlignVCenter, tr("sync ...")); painter.drawPixmap(m_pause_x, m_pause_y, QIcon::fromTheme("media-playback-pause-symbolic").pixmap(m_btn_size, m_btn_size)); } else { painter.drawText(m_text_x, m_text_y, m_text_w, m_text_height, Qt::AlignLeft | Qt::AlignVCenter, m_dest_uri); } // paint progress double value = m_current_value * m_progress_width; pen.setStyle(Qt::SolidLine); painter.setBrush(Qt::NoBrush); painter.setPen(pen); painter.setPen(Qt::NoPen); painter.setBrush(QBrush(btn->palette().color(QPalette::Button))); painter.drawRect(m_progress_x, m_progress_y, m_progress_width, m_progress_height); painter.setBrush(QBrush(btn->palette().color(QPalette::Highlight))); painter.drawRoundedRect(m_progress_x, m_progress_y, value, m_progress_height, 1, 1); if (!m_sync) { if (m_pause) { painter.drawPixmap(m_pause_x, m_pause_y, QIcon::fromTheme("media-playback-start-symbolic").pixmap(m_btn_size, m_btn_size)); } else { painter.drawPixmap(m_pause_x, m_pause_y, QIcon::fromTheme("media-playback-pause-symbolic").pixmap(m_btn_size, m_btn_size)); } } // paint close painter.drawPixmap(m_close_x, m_close_y, m_btn_size, m_btn_size, drawSymbolicColoredPixmap(QIcon::fromTheme("window-close-symbolic").pixmap(m_btn_size, m_btn_size))); painter.restore(); Q_UNUSED(event); } void ProgressBar::mouseReleaseEvent(QMouseEvent *event) { QPoint pos = event->pos(); if ((pos.x() >= m_pause_x) && (pos.x() <= m_pause_x_r) && (pos.y() >= m_pause_y) && (pos.y() <= m_pause_y_b)) { if (m_pause) { m_pause = false; Q_EMIT resume(); } else { m_pause = true; Q_EMIT pause(); } } else if ((pos.x() >= m_close_x) && (pos.x() <= m_close_x_r) && (pos.y() >= m_close_y) && (pos.y() <= m_close_y_b)) { /** * @note * fix: During the process of copying a file, * click the Cancel button in the small progress bar, * wait until the copy is finished and then click "OK" in the pop-up box, * which will cause the file manager to crash. * * Therefore, remove the uncheck pop-up * * @todo * Pause the file operation before confirming the popbox */ /*QMessageBox msgBox(QMessageBox::Warning, tr("cancel file operation"), tr("Are you sure want to cancel the current selected file operation"), QMessageBox::Ok | QMessageBox::Cancel, this); msgBox.button(QMessageBox::Ok)->setText(tr("OK")); msgBox.button(QMessageBox::Cancel)->setText(tr("Cancel")); if (QMessageBox::Ok == msgBox.exec() && */ // if(!m_is_stopping) { if (m_has_finished) return; m_is_stopping = true; Q_EMIT cancelled(); if (m_current_value <= 0) { Q_EMIT finished(this); } } } QWidget::mouseReleaseEvent(event); if (! m_is_stopping) update(); } void ProgressBar::onCancelled() { m_is_stopping = true; update(); } void ProgressBar::updateValue(double value) { if (value >= 0 && value < 1) { m_sync = false; m_current_value = value; } Q_EMIT sendValue(m_dest_uri, getIcon(), m_current_value); update(); } void ProgressBar::onElementFoundOne(const QString &uri, const qint64 &size) { ++m_total_count; m_total_size += size; m_src_uri = Peony::FileUtils::urlDecode(uri); //char* format_size = g_format_size (quint64(m_total_size)); //Calculated by 1024 bytes char* format_size = strtok(g_format_size_full(quint64(m_total_size),G_FORMAT_SIZE_IEC_UNITS),"iB"); g_free(format_size); } void ProgressBar::onElementFoundAll() { } void ProgressBar::onFileOperationProgressedOne(const QString &uri, const QString &destUri, const qint64 &size) { ++m_current_count; Q_UNUSED(uri); Q_UNUSED(size); Q_UNUSED(destUri); } void ProgressBar::updateProgress(const QString &srcUri, const QString &destUri, const QString& fIcon, const quint64& current, const quint64& total) { if (current >= m_total_size) { qDebug() << "progress bar value error!"; return; } m_src_uri = Peony::FileUtils::urlDecode(srcUri); if (nullptr != destUri) { m_dest_uri = Peony::FileUtils::urlDecode(destUri); } if (fIcon != getIcon().name()) { setIcon(fIcon); } double currentPercent = current * 1.0 / total; // qDebug() << "progress bar: " << currentPercent; updateValue(currentPercent); Q_UNUSED(srcUri); Q_UNUSED(destUri); } void ProgressBar::onFileOperationProgressedAll() { } void ProgressBar::onElementClearOne(const QString &uri) { Q_UNUSED(uri); } void ProgressBar::switchToRollbackPage() { } void ProgressBar::onStartSync() { m_sync = true; } void ProgressBar::onFinished() { hide(); Q_EMIT finished(this); } void ProgressBar::onFileRollbacked(const QString &destUri, const QString &srcUri) { Q_UNUSED(srcUri); Q_UNUSED(destUri); } QPixmap drawSymbolicColoredPixmap (const QPixmap& source) { // 18, 32, 69 QColor baseColor = btn->palette().color(QPalette::Text).light(150); QImage img = source.toImage(); for (int x = 0; x < img.width(); ++x) { for (int y = 0; y < img.height(); ++y) { auto color = img.pixelColor(x, y); color.setRed(baseColor.red()); color.setGreen(baseColor.green()); color.setBlue(baseColor.blue()); img.setPixelColor(x, y, color); } } return QPixmap::fromImage(img); } peony/libpeony-qt/file-operation/file-node-reporter.cpp0000644000175000017500000000173114205101223022206 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-node-reporter.h" #include "file-node.h" using namespace Peony; FileNodeReporter::FileNodeReporter(QObject *parent) : QObject(parent) { } FileNodeReporter::~FileNodeReporter() { } peony/libpeony-qt/file-operation/file-operation.pri0000644000175000017500000000364314205115226021445 0ustar fengfengINCLUDEPATH += $$PWD #include(../peony-core.pri) PKGCONFIG += gio-unix-2.0 HEADERS += \ $$PWD/file-node.h \ $$PWD/file-operation.h \ $$PWD/file-node-reporter.h \ $$PWD/file-link-operation.h \ $$PWD/file-copy-operation.h \ $$PWD/file-move-operation.h \ $$PWD/file-trash-operation.h \ $$PWD/file-count-operation.h \ $$PWD/file-delete-operation.h \ $$PWD/file-rename-operation.h \ $$PWD/file-operation-manager.h \ $$PWD/file-untrash-operation.h \ $$PWD/create-template-operation.h \ $$PWD/file-operation-progress-bar.h \ $$PWD/file-operation-error-dialog.h \ $$PWD/file-operation-error-handler.h \ $$PWD/file-operation-error-dialogs.h \ $$PWD/file-operation-progress-wizard.h \ $$PWD/file-operation-error-dialog-base.h \ SOURCES += \ $$PWD/file-node.cpp \ $$PWD/file-operation.cpp \ $$PWD/file-node-reporter.cpp \ $$PWD/file-link-operation.cpp \ $$PWD/file-move-operation.cpp \ $$PWD/file-copy-operation.cpp \ $$PWD/file-trash-operation.cpp \ $$PWD/file-count-operation.cpp \ $$PWD/file-delete-operation.cpp \ $$PWD/file-rename-operation.cpp \ $$PWD/file-operation-manager.cpp \ $$PWD/file-untrash-operation.cpp \ $$PWD/create-template-operation.cpp \ $$PWD/file-operation-progress-bar.cpp \ $$PWD/file-operation-error-dialog.cpp \ $$PWD/file-operation-error-dialogs.cpp \ $$PWD/file-operation-progress-wizard.cpp \ $$PWD/file-operation-error-dialog-base.cpp \ peony/libpeony-qt/file-operation/file-operation-manager.h0000644000175000017500000001555214205101223022504 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILEOPERATIONMANAGER_H #define FILEOPERATIONMANAGER_H #include #include #include #include #include #include "file-utils.h" #include "file-operation.h" #include "gerror-wrapper.h" #include "gobject-template.h" #include "peony-core_global.h" #include "file-operation-progress-bar.h" #include "file-operation-error-dialogs.h" namespace Peony { class FileOperationInfo; class FileWatcher; class FileOperation; /*! * \brief The FileOperationManager class * \details * In peony, the undo/redo stack manager is bind with a directory view. * And in peony-qt, it is similar to peony. But there are higher level * api to manage these 'managers' in peony-qt. * Not only the undo/redo stacks' management. FileOperationManager * only allows up to one file operation instace to run at same times, * this means the operations will be queue executed. * FileOperationManager will provide the operation-ui and error-handler-ui * which are implement as defaut in peony-qt's operation frameworks. * \note * FileOperationManager is not a strong binding in peony-qt's file operation * framework. If not considerring the operation-safety, you can just use a * QThreadPool instance to start over one operations at same time. * In this case you also should re-implement the operation wizard interface and * error handler interface. For example, do nothing at wizard (just not create * a wizard and not connect the related signal) and let the error handler * always response error type as ignore. */ class PEONYCORESHARED_EXPORT FileOperationManager : public QObject { Q_OBJECT public: static FileOperationManager *getInstance(); void close(); void setAllowParallel(bool allow = true); bool isAllowParallel(); Q_SIGNALS: void closed(); void operationStarted(std::shared_ptr info); void operationFinished(std::shared_ptr info, bool successed); public Q_SLOTS: void startOperation(FileOperation *operation, bool addToHistory = true); void startUndoOrRedo(std::shared_ptr info); bool canUndo(); std::shared_ptr getUndoInfo(); void undo(); bool canRedo(); std::shared_ptr getRedoInfo(); void redo(); void clearHistory(); void onFilesDeleted(const QStringList &uris); void handleError(FileOperationError& error); /*! * \brief registerFileWatcher * \param watcher * \details * For some limitation of gvfs/gio, some directory doesn't * support monitor yet. So we have to handle the data changed * by ourselves. * * The main idea is, every view will hold a watcher, and watcher * will be register to operation manager instance. * * everytime the file operation finished, watcher which not support * monitor will recived a signal from operation manager. And the view * will response the signal as same as other directories which allow monitor * action. */ void registerFileWatcher(FileWatcher *watcher); /*! * \brief unregisterFileWatcher * \param watcher * \details * when a file watcher deconstructed, it should be unregistered. * \see registerFileWatcher() */ void unregisterFileWatcher(FileWatcher *watcher); /*! * \brief manuallyNotifyDirectoryChanged * \param info * \details * real action to notify directory changed for directory * not support monitoring. */ void manuallyNotifyDirectoryChanged(FileOperationInfo *info); private: explicit FileOperationManager(QObject *parent = nullptr); ~FileOperationManager(); static void systemSleep (GDBusConnection* connection, const gchar* senderName, const gchar* objectPath, const gchar* interfaceName, const gchar* signalName, GVariant* parameters, gpointer udata); private: QThreadPool *m_thread_pool; bool m_allow_parallel = false; QVector m_watchers; bool m_is_current_operation_errored = false; FileOperationProgressBar *m_progressbar = nullptr; QStack> m_undo_stack; QStack> m_redo_stack; }; class FileOperationInfo : public QObject { Q_OBJECT friend class FileOperationManager; friend class FileOperation; public: QMap m_node_map; enum Type { Invalid, Move,//move back if no error in original moving Copy,//delete if no error in original copying Link,//delete... Rename,//rename Trash,//untrash Untrash,//trash Delete,//nothing to do CreateTxt,//delete CreateFolder,//delete CreateTemplate,//delete Other//nothing to do }; explicit FileOperationInfo(QStringList srcUris, QString destDirUri, Type type, QObject *parent = nullptr); explicit FileOperationInfo(QStringList srcUris, QStringList destDirUris, Type type, QObject *parent = nullptr); std::shared_ptr getOppositeInfo(FileOperationInfo *info); void oppositeInfoConstruct(Type type); void commonOppositeInfoConstruct(); void LinkOppositeInfoConstruct(); void RenameOppositeInfoConstruct(); void UntrashOppositeInfoConstruct(); void trashOppositeInfoConstruct(); Type operationType() { return m_type; } QStringList sources() { return m_src_uris; } QStringList dests() { return m_dest_uris; } QString target() { return m_dest_dir_uri; } //private: //normal operation src uris and dest uris QStringList m_src_uris; QStringList m_dest_dir_uris; QString m_dest_dir_uri; //FIXME: if files in different src dir, how to deal with it? QStringList m_dest_uris; QString m_src_dir_uri; //QMutex m_mutex; Type m_type; Type m_opposite_type; bool m_enable = true; //Rename QString m_oldname = nullptr; QString m_newname = nullptr; bool m_has_error = false; //using for distiguist move action. Qt::DropAction m_drop_action = Qt::IgnoreAction; }; } #endif // FILEOPERATIONMANAGER_H peony/libpeony-qt/file-operation/file-operation-progress-bar.h0000644000175000017500000002257514205115226023513 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Jing Ding * */ #ifndef FILEOPERATIONPROGRESS_H #define FILEOPERATIONPROGRESS_H #include #include #include class ProgressBar; class OtherButton; class MainProgressBar; class FileOperationProgressBar : public QWidget { Q_OBJECT public: static FileOperationProgressBar* getInstance(); void removeAllProgressbar (); ProgressBar* addFileOperation(); void showProgress (ProgressBar& progress); void removeFileOperation(ProgressBar* progress); private: explicit FileOperationProgressBar(QWidget *parent = nullptr); ~FileOperationProgressBar(); void showMore (); protected: void showWidgetList(bool show); void mouseMoveEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; Q_SIGNALS: void pause(); void start(); void canceled(); public Q_SLOTS: void mainProgressChange(QListWidgetItem *item); void showDelay(int msec = 2000); public: bool m_error = false; private: // layout QVBoxLayout* m_main_layout = nullptr; // widget QListWidget* m_list_widget = nullptr; ProgressBar* m_current_main = nullptr; OtherButton* m_other_progressbar = nullptr; MainProgressBar* m_main_progressbar = nullptr; QMap* m_widget_list = nullptr; QMap* m_progress_list = nullptr; int m_show_items = 2; bool m_show_more = false; int m_progress_item_height = 62; int m_progress_list_heigth = 200; // ui QPoint m_position; int m_progress_size = 0; bool m_is_press = false; bool m_more = false; static FileOperationProgressBar* instance; }; class ProgressBar : public QWidget { friend FileOperationProgressBar; Q_OBJECT public: explicit ProgressBar (QWidget* parent = nullptr); void setIcon (const QString& icon); QIcon& getIcon(); bool getStatus(); float getProgress(); float getTotalSize(); QString getFileName(); bool isPause(); void setPause(); void setResume(); private: ~ProgressBar(); Q_SIGNALS: void pause(); void resume(); void cancelled(); void finished(ProgressBar* fop); void sendValue(QString&, QIcon&, double); protected: void paintEvent(QPaintEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; public Q_SLOTS: void onCancelled(); void updateValue(double); void onElementFoundOne (const QString &uri, const qint64 &size); void onElementFoundAll (); void onFileOperationProgressedOne(const QString &uri, const QString &destUri, const qint64 &size); void updateProgress(const QString &srcUri, const QString &destUri, const QString& fIcon, const quint64& current, const quint64& total); void onFileOperationProgressedAll(); void onElementClearOne(const QString &uri); void switchToRollbackPage(); void onStartSync(); void onFinished(); void onFileRollbacked(const QString &destUri, const QString &srcUri); private: bool m_has_finished = false; bool m_sync = false; int m_fix_width = 550; int m_fix_height = 62; int m_btn_size = 18; int m_margin_ud = 2; int m_margin_lr = 8; int m_icon_size = 32; int m_text_height = 20; int m_progress_width = 80; int m_progress_height = 6; int m_percent_width = 20; // const const float m_icon_x = m_margin_lr; const float m_icon_y = (m_fix_height - m_margin_ud * 2 - m_icon_size) / 2 + m_margin_ud; const float m_text_x = m_margin_lr * 2 + m_icon_size; const float m_text_y = (m_fix_height - m_margin_ud * 2 - m_text_height) / 2 + m_margin_ud; const float m_text_w = m_fix_width - m_margin_lr * 6 - m_icon_size - m_btn_size * 2 - m_progress_width - m_percent_width; const float m_progress_x = m_margin_lr * 3 + m_icon_size + m_text_w; const float m_progress_y = (m_fix_height - m_margin_ud * 2 - m_progress_height) / 2 + m_margin_ud; const float m_pause_x = m_margin_lr * 5 + m_icon_size + m_text_w + m_progress_width; const float m_pause_x_r = m_pause_x + m_btn_size; const float m_pause_y = (m_fix_height - m_margin_ud * 2 - m_btn_size) / 2 + m_margin_ud; const float m_pause_y_b = m_pause_y + m_btn_size; const float m_close_x = m_pause_x + m_margin_lr + m_btn_size; const float m_close_x_r = m_close_x + m_btn_size; const float m_close_y = m_pause_y; const float m_close_y_b = m_pause_y_b; // value QIcon m_icon; double m_current_value = 0.0; QString m_src_uri; QString m_dest_uri; int m_total_count = 0; int m_current_count = 1; quint64 m_total_size = 0; qint32 m_current_size = 0; bool m_pause = false; bool m_is_stopping = false; }; class MainProgressBar : public QWidget { Q_OBJECT public: explicit MainProgressBar(QWidget *parent = nullptr); void initPrarm(); void setFileIcon (QIcon& icon); void setTitle (QString title); void setPause(); void setResume(); void setIsSync(bool); void setProgress(float); void setFileName(QString); protected: void paintEvent(QPaintEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; private: void paintFoot (QPainter& painter); void paintHeader (QPainter& painter); void paintContent (QPainter& painter); void paintProgress (QPainter& painter); Q_SIGNALS: void minimized(); void closeWindow(); void pause(); void start(); public Q_SLOTS: void cancelld(); void updateValue (QString&, QIcon&, double); private: bool m_sync = false; // can modify // header QString m_title; int m_fix_width = 550; int m_fix_height = 176; int m_title_width = 480; int m_header_height = 30; // btn int m_btn_margin_top = 8; int m_btn_margin = 10; int m_btn_size = 16; // icon int m_icon_margin = 20; int m_icon_size = 64; // file name int m_file_name_height = 60; int m_file_name_margin = 10; // pase button int m_pause_btn_height = 20; // percent int m_percent_margin = 15; int m_percent_height = 50; // foot float m_foot_margin = 3; // calc const float m_minilize_button_x_l = m_fix_width - m_btn_margin * 2 - m_btn_size * 2; const float m_minilize_button_x_r = m_minilize_button_x_l + m_btn_size; const float m_minilize_button_y_t = m_btn_margin_top; const float m_minilize_button_y_b = m_btn_margin_top + m_btn_size; const float m_close_button_x_l = m_fix_width - m_btn_margin - m_btn_size; const float m_close_button_x_r = m_fix_width - m_btn_margin; const float m_close_button_y_t = m_btn_margin_top; const float m_close_button_y_b = m_btn_margin_top + m_btn_size; const float m_foot_progress_back_y = m_fix_height - m_foot_margin; const float m_text_area_x = (m_fix_width - m_title_width ) / 2 - 2; const float m_icon_area_y = (m_fix_height - m_icon_size) / 2; const float m_file_name_x = m_icon_margin + m_file_name_margin + m_icon_size; const float m_file_name_y = m_fix_height / 2 - m_file_name_height / 2; const float m_file_name_w = m_fix_width - m_icon_size - m_icon_margin - m_pause_btn_height * 2 - m_file_name_margin * 3; const float m_progress_pause_x = m_file_name_x + m_file_name_w + m_pause_btn_height; const float m_progress_pause_x_r = m_progress_pause_x + m_pause_btn_height; const float m_progress_pause_y = (m_fix_height - m_pause_btn_height) / 2; const float m_progress_pause_y_b = (m_fix_height + m_pause_btn_height) / 2; const float m_percent_x = m_fix_width - m_percent_margin - m_percent_height; const float m_percent_y = m_fix_height - m_foot_margin - m_percent_height - m_percent_margin / 5; // progress bool m_show = false; bool m_pause = false; float m_move_x = 0.5; bool m_stopping = false; float m_current_value = 0.0; QString m_file_name = tr("starting ..."); QIcon m_icon = QIcon::fromTheme("text"); }; class OtherButton : public QWidget { Q_OBJECT public: explicit OtherButton(QWidget *parent = nullptr); protected: void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; Q_SIGNALS: void clicked (bool show); private: // button height int m_icon_margin = 6; int m_icon_size = 10; int m_button_heigth = 24; // text width int m_text_length = 100; QString m_text = tr("Other queue"); // show bool m_show = false; bool m_is_press = true; }; #endif // FILEOPERATIONPROGRESS_H peony/libpeony-qt/file-operation/file-move-operation.cpp0000644000175000017500000011747214205115226022407 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-move-operation.h" #include "file-node-reporter.h" #include "file-node.h" #include "file-enumerator.h" #include "file-info.h" #include "file-operation-manager.h" #include #include using namespace Peony; static void handleDuplicate(FileNode *node) { node->setDestFileName(FileUtils::handleDuplicateName(node->destBaseName())); } FileMoveOperation::FileMoveOperation(QStringList sourceUris, QString destDirUri, QObject *parent) : FileOperation (parent) { m_source_uris = sourceUris; if(destDirUri.startsWith("favorite://"))/* favorite://xxx特殊处理,例如本机共享,bug#83353 */ destDirUri = FileUtils::getTargetUri(destDirUri); m_dest_dir_uri = FileUtils::urlEncode(destDirUri); m_info = std::make_shared(sourceUris, destDirUri, FileOperationInfo::Move); } FileMoveOperation::~FileMoveOperation() { if (m_reporter) delete m_reporter; } void FileMoveOperation::setCopyMove(bool copyMove) { m_copy_move = copyMove; } void FileMoveOperation::setAction(Qt::DropAction action) { m_move_action = action; m_info.get()->m_drop_action = action; switch (action) { case Qt::CopyAction: { m_info.get()->m_type = FileOperationInfo::Copy; m_info.get()->m_opposite_type = FileOperationInfo::Delete; break; } default: { m_info.get()->m_type = FileOperationInfo::Move; break; } } } void FileMoveOperation::progress_callback(goffset current_num_bytes, goffset total_num_bytes, FileMoveOperation *p_this) { if (total_num_bytes < current_num_bytes) return; QUrl url(p_this->m_current_src_uri); auto currnet = p_this->m_current_offset + current_num_bytes; auto total = p_this->m_total_szie; auto fileIconName = FileUtils::getFileIconName(p_this->m_current_src_uri, false); auto destFileName = FileUtils::isFileDirectory(p_this->m_current_dest_dir_uri) ? p_this->m_current_dest_dir_uri + "/" + url.fileName() : p_this->m_current_dest_dir_uri; // qDebug() << "move: " << p_this->m_current_src_uri << " --- " << destFileName << currnet << "/" << total << (float(currnet) / total); Q_EMIT p_this->FileProgressCallback(p_this->m_current_src_uri, destFileName, fileIconName, currnet, total); //format: move srcUri to destDirUri: curent_bytes(count) of total_bytes(count). } ExceptionResponse FileMoveOperation::prehandle(GError *err) { //setHasError(true); if (m_prehandle_hash.contains(err->code)) return m_prehandle_hash.value(err->code); return Other; } void FileMoveOperation::move() { if (isCancelled()) return; QList nodes; QList errNode; GError *err = nullptr; m_total_count = m_source_uris.count(); auto destDir = wrapGFile(g_file_new_for_uri(m_dest_dir_uri.toUtf8().constData())); // file move for (auto srcUri : m_source_uris) { if (isCancelled()) return; auto node = new FileNode(srcUri, nullptr, nullptr); auto srcFile = wrapGFile(g_file_new_for_uri(srcUri.toUtf8().constData())); char *base_name = g_file_get_basename(srcFile.get()->get()); auto destFile = wrapGFile(g_file_resolve_relative_path(destDir.get()->get(), base_name)); g_autofree char* destUri = g_file_get_uri(destFile.get()->get()); node->setDestUri(destUri); g_file_move(srcFile.get()->get(), destFile.get()->get(), m_default_copy_flag, getCancellable().get()->get(), GFileProgressCallback(progress_callback), this, &err); if (err) { errNode << node; } else { node->setState(FileNode::Handled); } nodes << node; } // file copy-delete goffset *total_size = new goffset(0); for (auto node : errNode) { if (isCancelled()) return; node->findChildrenRecursively(); node->computeTotalSize(total_size); } m_total_szie = *total_size; operationPreparedOne("", m_total_szie); delete total_size; operationPrepared(); if (!errNode.isEmpty()) { if (m_move_action == Qt::TargetMoveAction) { m_info.get()->m_type = FileOperationInfo::Move; } else { m_info.get()->m_type = FileOperationInfo::Copy; m_info.get()->m_opposite_type = FileOperationInfo::Delete; } } for (auto node : errNode) { if (isCancelled()) return; moveForceUseFallback(node); fileSync(node->uri(), node->destUri()); } operationStartSnyc(); #if 0 for (auto file : nodes) { if (isCancelled()) return; QString srcUri = file->uri(); m_current_count = nodes.indexOf(file) + 1; m_current_src_uri = srcUri; m_current_dest_dir_uri = m_dest_dir_uri; auto srcFile = wrapGFile(g_file_new_for_uri(srcUri.toUtf8().constData())); char *base_name = g_file_get_basename(srcFile.get()->get()); auto destFile = wrapGFile(g_file_resolve_relative_path(destDir.get()->get(), base_name)); char *dest_uri = g_file_get_uri(destFile.get()->get()); file->setDestUri(dest_uri); g_free(dest_uri); g_free(base_name); //retry: GError *err = nullptr; g_file_move(srcFile.get()->get(), destFile.get()->get(), m_default_copy_flag, getCancellable().get()->get(), GFileProgressCallback(progress_callback), this, &err); if (err) { errNode << file; } else { file->setState(FileNode::Handled); } if (err) { setHasError(true); auto errWrapper = GErrorWrapper::wrapFrom(err); switch (errWrapper.get()->code()) { case G_IO_ERROR_CANCELLED: { for (auto node : nodes) { delete node; } nodes.clear(); return; } case G_IO_ERROR_NOT_SUPPORTED: case G_IO_ERROR_WOULD_RECURSE: case G_IO_ERROR_EXISTS: { moveForceUseFallback(file); operationStartSnyc(); continue; } default: break; } int handle_type = prehandle(err); FileOperationError except; except.srcUri = srcUri; except.destDirUri = m_dest_dir_uri; except.isCritical = false; except.op = FileOpMove; except.title = tr("Move file error"); except.errorCode = err->code; except.errorStr = err->message; except.errorType = ET_GIO; if (handle_type == Other) { auto responseTypeWrapper = Invalid; if (G_IO_ERROR_EXISTS == err->code) { except.dlgType = ED_CONFLICT; Q_EMIT errored(except); responseTypeWrapper = except.respCode; } else { except.dlgType = ED_WARNING; Q_EMIT errored(except); responseTypeWrapper = except.respCode; } handle_type = responseTypeWrapper; //block until error has been handled. } GError *handled_err = nullptr; switch (handle_type) { case IgnoreOne: { file->setState(FileNode::Unhandled); file->setErrorResponse(IgnoreOne); //skip to next loop. break; } case IgnoreAll: { file->setState(FileNode::Unhandled); file->setErrorResponse(IgnoreOne); m_prehandle_hash.insert(err->code, IgnoreOne); break; } case OverWriteOne: { file->setState(FileNode::Handled); file->setErrorResponse(OverWriteOne); g_file_move(srcFile.get()->get(), destFile.get()->get(), GFileCopyFlags(G_FILE_COPY_NOFOLLOW_SYMLINKS| G_FILE_COPY_OVERWRITE), getCancellable().get()->get(), GFileProgressCallback(progress_callback), this, &handled_err); break; } case OverWriteAll: { file->setState(FileNode::Handled); file->setErrorResponse(OverWriteOne); g_file_move(srcFile.get()->get(), destFile.get()->get(), GFileCopyFlags(G_FILE_COPY_NOFOLLOW_SYMLINKS| G_FILE_COPY_OVERWRITE), getCancellable().get()->get(), GFileProgressCallback(progress_callback), this, &handled_err); m_prehandle_hash.insert(err->code, OverWriteOne); break; } case BackupOne: { file->setState(FileNode::Handled); file->setErrorResponse(BackupOne); // use custom name QString name = ""; QStringList extendStr = file->destBaseName().split("."); if (extendStr.length() > 0) { extendStr.removeAt(0); } QString endStr = extendStr.join("."); if (except.respValue.contains("name")) { name = except.respValue["name"].toString(); if (endStr != "" && name.endsWith(endStr)) { file->setDestFileName(name); } else if ("" != endStr && "" != name) { file->setDestFileName(name + "." + endStr); } } while (FileUtils::isFileExsit(file->destUri())) { handleDuplicate(file); file->resolveDestFileUri(m_dest_dir_uri); } auto handledDestFileUri = file->resolveDestFileUri(m_dest_dir_uri); while (FileUtils::isFileExsit(handledDestFileUri)) { handledDestFileUri = file->resolveDestFileUri(m_dest_dir_uri); } auto handledDestFile = wrapGFile(g_file_new_for_uri(handledDestFileUri.toUtf8())); g_file_copy(srcFile.get()->get(), handledDestFile.get()->get(), GFileCopyFlags(m_default_copy_flag|G_FILE_COPY_BACKUP), getCancellable().get()->get(), GFileProgressCallback(progress_callback), this, &handled_err); setHasError(false); break; } case BackupAll: { m_prehandle_hash.insert(err->code, BackupOne); goto retry; break; } case Retry: { goto retry; } case Cancel: { file->setState(FileNode::Unhandled); cancel(); break; } default: break; } except.srcUri = srcUri; except.errorType = ET_GIO; except.errorCode = err->code; except.errorStr = err->message; except.op = FileOpMove; except.title = tr("Move file error"); except.destDirUri = m_dest_dir_uri; except.isCritical = true; if (handled_err) { auto handledErr = GErrorWrapper::wrapFrom(handled_err); FileOperationError except; if (G_IO_ERROR_EXISTS == handled_err->code) { except.dlgType = ED_CONFLICT; Q_EMIT errored(except); } else { except.dlgType = ED_WARNING; Q_EMIT errored(except); } auto response = except.respCode; } } else { file->setState(FileNode::Handled); } //FIXME: ignore the total size when using native move. operationProgressedOne(file->uri(), file->destUri(), 0); fileSync(file->uri(), file->destUri()); } #endif //native move has not clear operation. operationProgressed(); //FIXME: if native move function get into error, //such as the target is existed, the rollback might //get into error too. if (isCancelled()) { for (auto node : nodes) { rollbackNodeRecursively(node); } } //release node m_info.get()->m_src_uris.clear(); m_info.get()->m_dest_uris.clear(); for (auto file : nodes) { m_info.get()->m_src_uris<uri(); m_info.get()->m_dest_uris<destUri(); delete file; } nodes.clear(); } void FileMoveOperation::rollbackNodeRecursively(FileNode *node) { if (node->isFolder()) { if (node->state() == FileNode::Handled) { auto dir = wrapGFile(g_file_new_for_uri(node->uri().toUtf8().constData())); g_file_make_directory(dir.get()->get(), nullptr, nullptr); } for (auto child : *node->children()) { rollbackNodeRecursively(child); } if (node->responseType() != OverWriteOne && node->responseType() != OverWriteAll && !isCancelled()) { auto destDir = wrapGFile(g_file_new_for_uri(node->destUri().toUtf8().constData())); g_file_delete(destDir.get()->get(), nullptr, nullptr); } operationRollbackedOne(node->destUri(), node->uri()); } else { switch (node->state()) { case FileNode::Handled: { auto sourceFile = wrapGFile(g_file_new_for_uri(node->uri().toUtf8().constData())); auto destFile = wrapGFile(g_file_new_for_uri(node->destUri().toUtf8().constData())); if (node->responseType() == OverWriteOne || node->responseType() == OverWriteAll) { // note: this won't fully rollback, the file which has been overwriten will not be recovered. g_file_copy(destFile.get()->get(), sourceFile.get()->get(), m_default_copy_flag, nullptr, nullptr, nullptr, nullptr); break; } else { g_file_move(destFile.get()->get(), sourceFile.get()->get(), m_default_copy_flag, nullptr, nullptr, nullptr, nullptr); } break; } case FileNode::Handling: { if (node->responseType() == OverWriteOne || node->responseType() == OverWriteAll || node->responseType() == Cancel) { break; } auto destFile = wrapGFile(g_file_new_for_uri(node->destUri().toUtf8().constData())); g_file_delete(destFile.get()->get(), nullptr, nullptr); break; } default: break; } } // switch (node->state()) { // case FileNode::Handling: { // break; // } // case FileNode::Handled: { // //do not clear the dest file if ignored or overwrite or backuped. // if (node->responseType() != Other) // break; // if (node->isFolder()) { // auto children = node->children(); // for (auto child : *children) { // rollbackNodeRecursively(child); // } // GFile *dest_file = g_file_new_for_uri(node->destUri().toUtf8().constData()); // g_file_delete(dest_file, nullptr, nullptr); // g_object_unref(dest_file); // } else { // GFile *dest_file = g_file_new_for_uri(node->destUri().toUtf8().constData()); // g_file_delete(dest_file, nullptr, nullptr); // g_object_unref(dest_file); // } // operationRollbackedOne(node->destUri(), node->uri()); // break; // } // case FileNode::Cleared: { // switch (node->responseType()) { // case Other: { // if (node->isFolder()) { // GFile *src_file = g_file_new_for_uri(node->uri().toUtf8().constData()); // g_file_make_directory(src_file, nullptr, nullptr); // g_object_unref(src_file); // auto children = node->children(); // for (auto child : *children) { // rollbackNodeRecursively(child); // } // //try deleting the dest directory // GFile *dest_file = g_file_new_for_uri(node->destUri().toUtf8().constData()); // g_file_delete(dest_file, nullptr, nullptr); // g_object_unref(dest_file); // } else { // GFile *dest_file = g_file_new_for_uri(node->destUri().toUtf8().constData()); // GFile *src_file = g_file_new_for_uri(node->uri().toUtf8().constData()); // //"rollback" // GError *err = nullptr; // g_file_move(dest_file, // src_file, // m_default_copy_flag, // nullptr, // nullptr, // nullptr, // &err); // if (err) { // qDebug()<destUri(); // qDebug()<uri(); // qDebug()<message; // g_error_free(err); // } // g_object_unref(dest_file); // g_object_unref(src_file); // } // operationRollbackedOne(node->destUri(), node->uri()); // break; // } // default: { // //copy if err handle response type is valid. // if (node->isFolder()) { // GFile *src_file = g_file_new_for_uri(node->uri().toUtf8().constData()); // g_file_make_directory(src_file, nullptr, nullptr); // g_object_unref(src_file); // auto children = node->children(); // for (auto child : *children) { // rollbackNodeRecursively(child); // } // GFile *dest_file = g_file_new_for_uri(node->destUri().toUtf8().constData()); // g_object_unref(dest_file); // } else { // GFile *dest_file = g_file_new_for_uri(node->destUri().toUtf8().constData()); // GFile *src_file = g_file_new_for_uri(node->uri().toUtf8().constData()); // //"rollback" // GError *err = nullptr; // g_file_copy(dest_file, // src_file, // m_default_copy_flag, // nullptr, // nullptr, // nullptr, // &err); // if (err) { // qDebug()<destUri(); // qDebug()<uri(); // qDebug()<message; // g_error_free(err); // } // g_object_unref(dest_file); // g_object_unref(src_file); // } // operationRollbackedOne(node->destUri(), node->uri()); // break; // } // } // break; // } // default: { // //make sure all nodes were rollbacked. // if (node->isFolder()) { // auto children = node->children(); // for (auto child : *children) { // rollbackNodeRecursively(child); // } // } // break; // } // } } void FileMoveOperation::copyRecursively(FileNode *node) { if (isCancelled()) return; node->setState(FileNode::Handling); QString relativePath = node->getRelativePath(); //FIXME: the smart pointers' deconstruction spends too much time. GFileWrapperPtr destRoot = wrapGFile(g_file_new_for_uri(m_dest_dir_uri.toUtf8().constData())); GFileWrapperPtr destFile = wrapGFile(g_file_resolve_relative_path(destRoot.get()->get(), relativePath.toUtf8().constData())); char *dest_file_uri = g_file_get_uri(destFile.get()->get()); node->setDestUri(dest_file_uri); g_free(dest_file_uri); m_current_src_uri = node->uri(); GFile *dest_parent = g_file_get_parent(destFile.get()->get()); char *dest_dir_uri = g_file_get_uri(dest_parent); m_current_dest_dir_uri = dest_dir_uri; g_free(dest_dir_uri); g_object_unref(dest_parent); QString destName = ""; fallback_retry: if (node->isFolder()) { auto realDestUri = node->resolveDestFileUri(m_dest_dir_uri); destFile = wrapGFile(g_file_new_for_uri(realDestUri.toUtf8().constData())); GError *err = nullptr; auto fileIconName = FileUtils::getFileIconName(m_current_src_uri, false); auto destFileName = FileUtils::isFileDirectory(m_current_dest_dir_uri) ? nullptr : m_current_dest_dir_uri; //NOTE: mkdir doesn't have a progress callback. Q_EMIT FileProgressCallback(m_current_src_uri, destFileName, fileIconName, node->size(), node->size()); g_file_make_directory(destFile.get()->get(),getCancellable().get()->get(), &err); if (err) { setHasError(true); FileOperationError except; if (err->code == G_IO_ERROR_CANCELLED) { return; } auto errWrapperPtr = GErrorWrapper::wrapFrom(err); int handle_type = prehandle(err); except.errorType = ET_GIO; except.op = FileOpMove; except.title = tr("Move file error"); except.errorCode = err->code; except.errorStr = err->message; except.srcUri = m_current_src_uri; except.destDirUri = m_current_dest_dir_uri; except.isCritical = false; if (handle_type == Other) { auto typeData = Invalid; if (G_IO_ERROR_EXISTS == err->code) { except.dlgType = ED_CONFLICT; Q_EMIT errored(except); typeData = except.respCode; } else { except.dlgType = ED_WARNING; Q_EMIT errored(except); typeData = except.respCode; } // ignore multiple bounces if (except.errorCode == G_IO_ERROR_NOT_SUPPORTED) { m_prehandle_hash.insert(err->code, IgnoreOne); } handle_type = typeData; } //handle. switch (handle_type) { case IgnoreOne: { node->setState(FileNode::Unhandled); node->setErrorResponse(IgnoreOne); break; } case IgnoreAll: { node->setState(FileNode::Unhandled); node->setErrorResponse(IgnoreOne); m_prehandle_hash.insert(err->code, IgnoreOne); break; } case OverWriteOne: { //node->setState(FileNode::Handled); node->setErrorResponse(OverWriteOne); //make dir has no overwrite break; } case OverWriteAll: { //node->setState(FileNode::Handled); node->setErrorResponse(OverWriteOne); m_prehandle_hash.insert(err->code, OverWriteOne); break; } case BackupOne: { //node->setState(FileNode::Handled); node->setErrorResponse(BackupOne); // use custom name QString name = ""; QStringList extendStr = node->destBaseName().split("."); if (extendStr.length() > 0) { extendStr.removeAt(0); } QString endStr = extendStr.join("."); if (except.respValue.contains("name")) { name = except.respValue["name"].toString(); if (endStr != "" && name.endsWith(endStr)) { node->setDestFileName(name); } else if ("" != endStr && "" != name) { node->setDestFileName(name + "." + endStr); } else if ("" == endStr) { node->setDestFileName(name); } } node->resolveDestFileUri(m_dest_dir_uri); while (FileUtils::isFileExsit(node->destUri())) { handleDuplicate(node); node->resolveDestFileUri(m_dest_dir_uri); } g_object_unref(destFile.get()); destFile = wrapGFile(g_file_new_for_uri(node->destUri().toUtf8().constData())); setHasError(false); goto fallback_retry; } case BackupAll: { m_prehandle_hash.insert(err->code, BackupOne); goto fallback_retry; } case Retry: { goto fallback_retry; } case Cancel: { node->setState(FileNode::Unhandled); cancel(); break; } default: break; } } else { //node->setState(FileNode::Handled); } fileIconName = FileUtils::getFileIconName(m_current_src_uri, false); destFileName = FileUtils::isFileDirectory(m_current_dest_dir_uri) ? nullptr : m_current_dest_dir_uri; //assume that make dir finished anyway m_current_offset += node->size(); Q_EMIT FileProgressCallback(m_current_src_uri, destFileName, fileIconName, m_current_offset, m_total_szie); Q_EMIT operationProgressedOne(node->uri(), node->destUri(), node->size()); for (auto child : *(node->children())) { copyRecursively(child); } } else { GError *err = nullptr; GFileWrapperPtr sourceFile = wrapGFile(g_file_new_for_uri(node->uri().toUtf8().constData())); auto realDestUri = node->resolveDestFileUri(m_dest_dir_uri); destFile = wrapGFile(g_file_new_for_uri(realDestUri.toUtf8().constData())); FileCopy fileCopy (node->uri(), realDestUri, m_default_copy_flag, getCancellable().get()->get(), GFileProgressCallback(progress_callback), this, &err); fileCopy.connect(this, &FileOperation::operationPause, &fileCopy, &FileCopy::pause, Qt::DirectConnection); fileCopy.connect(this, &FileOperation::operationResume, &fileCopy, &FileCopy::resume, Qt::DirectConnection); fileCopy.connect(this, &FileOperation::operationCancel, &fileCopy, &FileCopy::cancel, Qt::DirectConnection); if (m_is_pause) fileCopy.pause(); fileCopy.run(); if (err) { setHasError(true); switch (err->code) { case G_IO_ERROR_CANCELLED: return; case G_IO_ERROR_INVALID_FILENAME: { QString newDestUri; if (makeFileNameValidForDestFS(m_current_src_uri, m_dest_dir_uri, &newDestUri)) { if (newDestUri != destName) { destName = newDestUri; node->setDestFileName(newDestUri); goto fallback_retry; } } break; } } FileOperationError except; auto errWrapperPtr = GErrorWrapper::wrapFrom(err); int handle_type = prehandle(err); except.isCritical = true; except.errorType = ET_GIO; except.errorCode = err->code; except.errorStr = err->message; except.op = FileOpMove; except.title = tr("Create file error"); except.srcUri = m_current_src_uri; except.destDirUri = m_current_dest_dir_uri; if (handle_type == Other) { auto typeData = Invalid; if (G_IO_ERROR_EXISTS == err->code) { except.dlgType = ED_CONFLICT; Q_EMIT errored(except); typeData = except.respCode; } else { except.dlgType = ED_WARNING; Q_EMIT errored(except); typeData = except.respCode; } handle_type = typeData; } //handle. switch (handle_type) { case IgnoreOne: { node->setState(FileNode::Unhandled); node->setErrorResponse(IgnoreOne); break; } case IgnoreAll: { node->setState(FileNode::Unhandled); node->setErrorResponse(IgnoreOne); m_prehandle_hash.insert(err->code, IgnoreOne); break; } case OverWriteOne: { FileCopy fileCopy (node->uri(), realDestUri, GFileCopyFlags(m_default_copy_flag | G_FILE_COPY_OVERWRITE), getCancellable().get()->get(), GFileProgressCallback(progress_callback), this, &err); fileCopy.connect(this, &FileOperation::operationPause, &fileCopy, &FileCopy::pause, Qt::DirectConnection); fileCopy.connect(this, &FileOperation::operationResume, &fileCopy, &FileCopy::resume, Qt::DirectConnection); fileCopy.connect(this, &FileOperation::operationCancel, &fileCopy, &FileCopy::cancel, Qt::DirectConnection); if (m_is_pause) fileCopy.pause(); fileCopy.run(); node->setErrorResponse(OverWriteOne); break; } case OverWriteAll: { // g_file_copy(sourceFile.get()->get(), // destFile.get()->get(), // GFileCopyFlags(m_default_copy_flag | G_FILE_COPY_OVERWRITE), // getCancellable().get()->get(), // GFileProgressCallback(progress_callback), // this, // nullptr); FileCopy fileCopy (node->uri(), realDestUri, GFileCopyFlags(m_default_copy_flag | G_FILE_COPY_OVERWRITE), getCancellable().get()->get(), GFileProgressCallback(progress_callback), this, &err); fileCopy.connect(this, &FileOperation::operationPause, &fileCopy, &FileCopy::pause, Qt::DirectConnection); fileCopy.connect(this, &FileOperation::operationResume, &fileCopy, &FileCopy::resume, Qt::DirectConnection); fileCopy.connect(this, &FileOperation::operationCancel, &fileCopy, &FileCopy::cancel, Qt::DirectConnection); if (m_is_pause) fileCopy.pause(); fileCopy.run(); //node->setState(FileNode::Handled); node->setErrorResponse(OverWriteOne); m_prehandle_hash.insert(err->code, OverWriteOne); break; } case BackupOne: { // use custom name QString name = ""; QStringList extendStr = node->destBaseName().split("."); if (extendStr.length() > 0) { extendStr.removeAt(0); } QString endStr = extendStr.join("."); if (except.respValue.contains("name")) { name = except.respValue["name"].toString(); if (endStr != "" && name.endsWith(endStr)) { node->setDestFileName(name); } else if ("" != endStr && "" != name) { node->setDestFileName(name + "." + endStr); } } while (FileUtils::isFileExsit(node->destUri())) { 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())); FileCopy fileCopy (node->uri(), realDestUri, GFileCopyFlags(m_default_copy_flag | G_FILE_COPY_BACKUP), getCancellable().get()->get(), GFileProgressCallback(progress_callback), this, &err); fileCopy.connect(this, &FileOperation::operationPause, &fileCopy, &FileCopy::pause, Qt::DirectConnection); fileCopy.connect(this, &FileOperation::operationResume, &fileCopy, &FileCopy::resume, Qt::DirectConnection); fileCopy.connect(this, &FileOperation::operationCancel, &fileCopy, &FileCopy::cancel, Qt::DirectConnection); if (m_is_pause) fileCopy.pause(); fileCopy.run(); //node->setState(FileNode::Handled); node->setErrorResponse(BackupOne); setHasError(false); break; } case BackupAll: { m_prehandle_hash.insert(err->code, BackupOne); goto fallback_retry; break; } case Retry: { goto fallback_retry; } case Cancel: { node->setErrorResponse(Cancel); cancel(); break; } default: break; } } fileSync(node->uri(), realDestUri); m_current_offset += node->size(); auto fileIconName = FileUtils::getFileIconName(m_current_src_uri, false); auto destFileName = FileUtils::isFileDirectory(node->destUri()) ? nullptr : node->destUri(); Q_EMIT FileProgressCallback(node->uri(), destFileName, fileIconName, m_current_offset, m_total_szie); Q_EMIT operationProgressedOne(node->uri(), node->destUri(), node->size()); } destFile.reset(); destRoot.reset(); } void FileMoveOperation::deleteRecursively(FileNode *node) { if (isCancelled()) return; g_autoptr(GFile) file = g_file_new_for_uri(node->uri().toUtf8().constData()); if (node->isFolder()) { for (auto child : *(node->children())) { deleteRecursively(child); } if (node->state() != FileNode::Unhandled) { g_file_delete(file, getCancellable().get()->get(), nullptr); node->setState(FileNode::Handled); } } else { if (node->state() != FileNode::Unhandled) { g_file_delete(file, getCancellable().get()->get(), nullptr); node->setState(FileNode::Handled); } } operationAfterProgressedOne(node->uri()); } void FileMoveOperation::moveForceUseFallback() { if (isCancelled()) return; Q_EMIT operationRequestShowWizard(); m_reporter = new FileNodeReporter; connect(m_reporter, &FileNodeReporter::nodeFound, this, &FileMoveOperation::operationPreparedOne); //FIXME: total size should not compute twice. I should get it from ui-thread. goffset *total_size = new goffset(0); QList nodes; for (auto uri : m_source_uris) { FileNode *node = new FileNode(uri, nullptr, m_reporter); node->findChildrenRecursively(); node->computeTotalSize(total_size); nodes<m_type = FileOperationInfo::Move; for (auto node : nodes) { deleteRecursively(node); } } else { m_info.get()->m_type = FileOperationInfo::Copy; m_info.get()->m_opposite_type = FileOperationInfo::Delete; } if (isCancelled()) Q_EMIT operationStartRollbacked(); for (auto file : nodes) { qDebug()<uri(); if (isCancelled()) { rollbackNodeRecursively(file); } } m_info.get()->m_src_uris.clear(); m_info.get()->m_dest_uris.clear(); for (auto node : nodes) { m_info.get()->m_src_uris<uri(); m_info.get()->m_dest_uris<destUri(); delete node; } nodes.clear(); } void FileMoveOperation::moveForceUseFallback(FileNode* node) { if (isCancelled() || nullptr == node) return; operationPrepared(); copyRecursively(node); if (isCancelled()) { Q_EMIT operationStartRollbacked(); } if (m_move_action == Qt::TargetMoveAction) { deleteRecursively(node); } node->setState(FileNode::Handled); if (isCancelled()) { rollbackNodeRecursively(node); } } bool FileMoveOperation::isValid() { int index = 0; bool isInvalid = false; for (auto srcUri : m_source_uris) { auto srcFile = wrapGFile(g_file_new_for_uri(srcUri.toUtf8().constData())); auto destFile = wrapGFile(g_file_new_for_uri(m_dest_dir_uri.toUtf8().constData())); auto parentFile = wrapGFile(g_file_get_parent(srcFile.get()->get())); if (g_file_equal(destFile.get()->get(), parentFile.get()->get())) { m_source_uris.removeAt(index); --index; } //BUG: some special basename like test and test2, will lead the operation invalid. /* if (m_dest_dir_uri.contains(srcUri)) { isInvalid = true; invalidOperation(tr("Invalid move operation, cannot move a file into its sub directories.")); } */ //FIXME: find if destUriDirFile is srcFile's child. //it will call G_IO_ERROR_INVALID_FILENAME ++index; } if (isInvalid) invalidExited(tr("Invalid Operation.")); return !isInvalid; } void FileMoveOperation::run() { Q_EMIT operationStarted(); start: if (!isValid()) { FileOperationError except; except.errorType = ET_GIO; except.dlgType = ED_WARNING; except.srcUri = nullptr; except.destDirUri = nullptr; except.op = FileOpMove; except.title = tr("File delete error"); except.errorCode = G_IO_ERROR_INVAL; except.errorStr = tr("Invalid Operation"); Q_EMIT errored(except); auto response = except.respCode; switch (response) { case Retry: goto start; case Cancel: cancel(); break; default: break; } goto end; } if (isCancelled()) return; //should block and wait for other object prepared. move(); // if (!m_force_use_fallback) { // move(); // } qDebug()<<"finished"; end: Q_EMIT operationFinished(); } void FileMoveOperation::cancel() { FileOperation::cancel(); if (m_reporter) m_reporter->cancel(); } peony/libpeony-qt/file-operation/file-operation-error-dialog.h0000644000175000017500000000372514205101223023457 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILEOPERATIONERRORDIALOG_H #define FILEOPERATIONERRORDIALOG_H #include #include "file-operation-error-handler.h" class QFormLayout; class QLabel; class QDialogButtonBox; class QButtonGroup; class QFontMetrics; namespace Peony { class PEONYCORESHARED_EXPORT FileOperationErrorDialog : public QDialog, public FileOperationErrorHandler { Q_OBJECT Q_INTERFACES(Peony::FileOperationErrorHandler) public: QFontMetrics *pfontMetrics; explicit FileOperationErrorDialog(QWidget *parent = nullptr); ~FileOperationErrorDialog() override; virtual void handle (FileOperationError& error) override; public Q_SLOTS: int handleError(const QString &srcUri, const QString &destDirUri, const GErrorWrapperPtr &err, bool isCritical = false); private: QFormLayout *m_layout = nullptr; QLabel *m_src_line = nullptr; QLabel *m_dest_line = nullptr; QLabel *m_err_line = nullptr; QDialogButtonBox *m_button_box = nullptr; QDialogButtonBox *m_button_box2 = nullptr; QButtonGroup *btGroup = nullptr; const int ELIDE_ERROR_TEXT_LENGTH = 32; }; } #endif // FILEOPERATIONERRORDIALOG_H peony/libpeony-qt/file-operation/file-copy-operation.h0000644000175000017500000000675514205101223022051 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILECOPYOPERATION_H #define FILECOPYOPERATION_H #include "peony-core_global.h" #include "file-operation.h" namespace Peony { class FileNodeReporter; class FileNode; /*! * \brief The FileCopyOperation class * \todo * implment duplicated copy. this should be consumed as the backup handler. */ class PEONYCORESHARED_EXPORT FileCopyOperation : public FileOperation { Q_OBJECT public: explicit FileCopyOperation(QStringList sourceUris, QString destDirUri, QObject *parent = nullptr); ~FileCopyOperation() override; void run() override; std::shared_ptr getOperationInfo() override { return m_info; } public Q_SLOTS: void cancel() override; protected: ExceptionResponse prehandle(GError *err); static void progress_callback(goffset current_num_bytes, goffset total_num_bytes, FileCopyOperation *p_this); /*! * \brief copyRecursively * \param node * \see FileMoveOperation::copyRecursively() */ void copyRecursively(FileNode *node); /*! * \brief rollbackNodeRecursively * \param node * \details * This function is similar to FileMoveOperation::rollbackNodeRecursively(), * but it is more simple. The copy operation's rollbacking logic is easier to * understand. */ void rollbackNodeRecursively(FileNode *node); private: /*! * \brief m_is_duplicated_copy * \details * In peony-qt, file copy operation has 2 types. * 1. The file is duplicated in same folder. * 2. Other. * If case 1, the peony-qt will trying to copy a file with special suffix automaticly. * For example, a file abc.xyz will be copied and renamed to abc(1).xyz, abc(2).xyz... * If case 2, there will be nothing special action in operation except it went into error. * \deprecated * use FileDuplicateOperation instead. */ bool m_is_duplicated_copy = false; QStringList m_source_uris; QString m_dest_dir_uri = nullptr; int m_current_count = 0; int m_total_count = 0; QSet m_conflict_files; QString m_current_src_uri = nullptr; QString m_current_dest_dir_uri = nullptr; goffset m_current_offset = 0; goffset m_total_szie = 0; GFileCopyFlags m_default_copy_flag = GFileCopyFlags(G_FILE_COPY_NOFOLLOW_SYMLINKS); FileNodeReporter *m_reporter = nullptr; /*! * \brief m_prehandle_hash * \details * Once a move operation get into error, this class might cache the specific response * for next prehandleing. */ QHash m_prehandle_hash; std::shared_ptr m_info = nullptr; }; } #endif // FILECOPYOPERATION_H peony/libpeony-qt/file-operation/create-template-operation.cpp0000644000175000017500000001635214205115226023573 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "create-template-operation.h" #include "file-operation-manager.h" #include "file-operation-utils.h" #include "global-settings.h" #include #include #include #include #include using namespace Peony; #define TEMPLATE_DIR "file://" + GlobalSettings::getInstance()->getValue(TEMPLATES_DIR).toString() void CreateTemplateOperation::handleDuplicate(const QString &uri) { m_target_uri = m_dest_dir_uri + "/" + FileUtils::handleDuplicateName(uri); } CreateTemplateOperation::CreateTemplateOperation(const QString &destDirUri, Type type, const QString &templateName, QObject *parent) : FileOperation(parent) { m_target_uri = destDirUri + "/" + templateName; QStringList srcUris; m_src_uri = TEMPLATE_DIR + templateName; srcUris << m_src_uri; m_dest_dir_uri = destDirUri; m_type = type; m_info = std::make_shared(srcUris, destDirUri, FileOperationInfo::Type::Copy); } void CreateTemplateOperation::run() { Q_EMIT operationStarted(); Q_EMIT operationPrepared(); switch (m_type) { case EmptyFile: { m_target_uri = m_dest_dir_uri + "/" + tr("NewFile") + ".txt"; retry_create_empty_file: 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) { FileOperationError except; if (err->code == G_IO_ERROR_EXISTS) { g_error_free(err); handleDuplicate(m_target_uri); goto retry_create_empty_file; } else { except.srcUri = m_src_uri; except.dlgType = ED_WARNING; except.destDirUri = m_dest_dir_uri; except.isCritical = true; except.op = FileOpCreateTemp; except.title = tr("Create file"); except.errorCode = err->code; except.errorStr = err->message; except.errorType = ET_GIO; Q_EMIT errored(except); } } //fix bug 35145, function occupy udisk issue g_object_unref(newFile); break; } case EmptyFolder: { m_target_uri = m_dest_dir_uri + "/" + tr("NewFolder"); retry_create_empty_folder: 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) { g_error_free(err); handleDuplicate(m_target_uri); goto retry_create_empty_folder; } else { FileOperationError except; except.srcUri = m_src_uri; except.dlgType = ED_WARNING; except.destDirUri = m_dest_dir_uri; except.isCritical = true; except.op = FileOpCreateTemp; except.title = tr("Create file error"); except.errorCode = err->code; except.errorStr = err->message; except.errorType = ET_GIO; Q_EMIT errored(except); } } break; } case Template: { retry_create_template: qDebug() << "create tmp"; 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(), GFileCopyFlags(G_FILE_COPY_NOFOLLOW_SYMLINKS), nullptr, nullptr, nullptr, &err); if (err) { setHasError(true); if (err->code == G_IO_ERROR_EXISTS) { g_error_free(err); handleDuplicate(m_target_uri); goto retry_create_template; } else { FileOperationError except; except.srcUri = m_src_uri; except.dlgType = ED_WARNING; except.destDirUri = m_dest_dir_uri; except.isCritical = true; except.op = FileOpCreateTemp; except.title = tr("Create file error"); except.errorCode = err->code; except.errorStr = err->message; except.errorType = ET_GIO; Q_EMIT errored(except); } } else { setHasError(false); } // change file's modify time and access time after copy templete file; time_t now_time = time(NULL); g_file_set_attribute_uint64(wrapGFile(g_file_new_for_uri(m_target_uri.toUtf8())).get()->get(), G_FILE_ATTRIBUTE_TIME_MODIFIED, (guint64)now_time, G_FILE_QUERY_INFO_NONE, nullptr, &err); break; } } // judge if the operation should sync. bool needSync = false; GFile *src_first_file = g_file_new_for_uri(FileUtils::urlEncode(m_src_uri).toUtf8().constData()); GMount *src_first_mount = g_file_find_enclosing_mount(src_first_file, nullptr, nullptr); if (src_first_mount) { needSync = g_mount_can_unmount(src_first_mount); g_object_unref(src_first_mount); } else { // maybe a vfs file. needSync = true; } g_object_unref(src_first_file); GFile *dest_dir_file = g_file_new_for_uri(FileUtils::urlEncode(m_dest_dir_uri).toUtf8().constData()); GMount *dest_dir_mount = g_file_find_enclosing_mount(dest_dir_file, nullptr, nullptr); if (src_first_mount) { needSync = g_mount_can_unmount(dest_dir_mount); g_object_unref(dest_dir_mount); } else { needSync = true; } g_object_unref(dest_dir_file); //needSync = true; if (needSync) { auto path = g_file_get_path(dest_dir_file); if (path) { operationStartSnyc(); QProcess p; p.start(QString("sync -f '%1'").arg(path)); p.waitForFinished(-1); g_free(path); } } // as target() m_info.get()->m_dest_dir_uri = m_target_uri; m_info.get()->m_dest_uris.clear(); m_info.get()->m_dest_uris<. * * Authors: Yue Lan * */ #ifndef FILERENAMEOPERATION_H #define FILERENAMEOPERATION_H #include "peony-core_global.h" #include "file-operation.h" namespace Peony { class PEONYCORESHARED_EXPORT FileRenameOperation : public FileOperation { Q_OBJECT public: /*! * \brief FileRenameOperation * \param uri * \param newName * \details * In most filemanagers, files always show their display name at directory view, * but there were a special kind of files not, the .desktop files might show * their names based on their contents. * Rename Operation have to both effect at the normal files and .desktop files(executable). * \note * Rename a .desktop file is very complex. Because it would change the contents of the file. * In GLib/GIO's api, it will lost some attribute if change the file contents. */ explicit FileRenameOperation(QString uri, QString newName); void setAutoOverwrite () { // m_auto_overwrite = true; m_apply_all = OverWriteAll; } void setAutoBackup () { m_apply_all = BackupAll; } void setAutoIgnore () { m_apply_all = IgnoreAll; } void run() override; std::shared_ptr getOperationInfo() override { return m_info; } private: ExceptionResponse prehandle(GError *err); GFileCopyFlags m_default_copy_flag = GFileCopyFlags(G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA); QString m_uri = nullptr; QString m_new_name = nullptr; QString m_old_name = nullptr; std::shared_ptr m_info = nullptr; ExceptionResponse m_apply_all = Other; }; } #endif // FILERENAMEOPERATION_H peony/libpeony-qt/file-operation/file-untrash-operation.cpp0000644000175000017500000003473314205115226023123 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-utils.h" #include "file-untrash-operation.h" #include "file-operation-manager.h" #include "file-info-job.h" #include "file-info.h" #include "file-meta-info.h" #include using namespace Peony; FileUntrashOperation::FileUntrashOperation(QStringList uris, QObject *parent) : FileOperation (parent) { m_uris = uris; //FIXME: should i put this into prepare process? cacheOriginalUri(); QStringList destUris; for (auto value : m_restore_hash) { destUris<(uris, destUris, FileOperationInfo::Untrash); } void FileUntrashOperation::cacheOriginalUri() { for (auto uri : m_uris) { if (isCancelled()) break; auto file = wrapGFile(g_file_new_for_uri(FileUtils::urlEncode(uri).toUtf8().constData())); auto info = wrapGFileInfo(g_file_query_info(file.get()->get(), G_FILE_ATTRIBUTE_TRASH_ORIG_PATH, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, getCancellable().get()->get(), nullptr)); auto origin_path = g_file_info_get_attribute_byte_string(info.get()->get(), G_FILE_ATTRIBUTE_TRASH_ORIG_PATH); auto destFile = wrapGFile(g_file_new_for_path(origin_path)); auto originUri = FileUtils::getFileUri(destFile); m_restore_hash.insert(uri, originUri); } } const QString FileUntrashOperation::handleDuplicate(const QString &uri) { setHasError(true); QStringList l = uri.split("/"); QString name = l.last(); l.removeLast(); QRegExp regExpNum("^\\(\\d+\\)"); QRegExp regExp("\\(\\d+\\)(\\.[0-9a-zA-Z]+|)$"); 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; name = name.replace(regExp, ext.replace(regExpNum, QString("(%1)").arg(num))); l.append(name); auto newUri = l.join("/"); return newUri; } else { if (name.contains(".")) { auto list = name.split("."); if (list.count() <= 1) { l.append(name + "(1)"); auto newUri = l.join("/"); return newUri; } else { int pos = list.count() - 1; if (list.last() == "gz" | list.last() == "xz" | list.last() == "Z" | list.last() == "sit" | list.last() == "bz" | list.last() == "bz2") { pos--; } if (pos < 0) pos = 0; //list.insert(pos, "(1)"); auto tmp = list; QStringList suffixList; for (int i = 0; i < list.count() - pos; i++) { suffixList.prepend(tmp.takeLast()); } auto suffix = suffixList.join("."); auto basename = tmp.join("."); name = basename + "(1)" + "." + suffix; if (name.endsWith(".")) name.chop(1); l.append(name); auto newUri = l.join("/"); return newUri; } } else { return uri + "(1)"; } } } ExceptionResponse FileUntrashOperation::prehandle(GError *err) { setHasError(true); if (m_prehandle_hash.contains(err->code)) return m_prehandle_hash.value(err->code); return Other; } void FileUntrashOperation::untrashFileErrDlg( FileOperationError &except, QString &srcUri, QString &originUri, GError *err) { except.srcUri = srcUri; if (nullptr != originUri){ except.destDirUri = originUri; } except.isCritical = false; except.op = FileOpUntrash; except.title = tr("Untrash file error"); except.errorCode = err->code; except.errorStr = err->message; except.errorType = ET_GIO; if (G_IO_ERROR_EXISTS == err->code) { except.dlgType = ED_CONFLICT; } else { except.dlgType = ED_WARNING; } Q_EMIT errored(except); } void FileUntrashOperation::getBackupName( QString &originUri, FileOperationError &except) { QString name = ""; QStringList extendStr; if (except.respValue.contains("name")) { name = except.respValue["name"].toString(); if (name.isEmpty()) { qDebug()<<"input file name is empty."; return; } int endIndex = originUri.lastIndexOf('/'); extendStr = originUri.split("."); if (extendStr.length() > 0) { extendStr.removeAt(0); } QString endStr = extendStr.join("."); if ("" != endStr) { endStr = "." + endStr; if (!name.endsWith(endStr)){ originUri = originUri.left(endIndex) + "/" + name + endStr; } else { originUri = originUri.left(endIndex) + "/" + name; } } else { originUri = originUri.left(endIndex) + "/" + name; } } return; } int FileUntrashOperation::copyFileProcess(QString &srcFile, QString &destFile) { int ret = 0; GError *err = nullptr; auto file = wrapGFile(g_file_new_for_uri(FileUtils::urlEncode(srcFile).toUtf8().constData())); auto originFile = wrapGFile(g_file_new_for_uri(FileUtils::urlEncode(destFile).toUtf8().constData())); g_file_copy(file.get()->get(), originFile.get()->get(), GFileCopyFlags(m_default_copy_flag|G_FILE_COPY_OVERWRITE), getCancellable().get()->get(), nullptr, nullptr, &err); if (err) { ret = -err->code; qWarning()<< "copy file :" << srcFile << " Error info:" << err->message << " Error code:" << ret; /* * 由于指定了G_FILE_COPY_OVERWRITE标志,因此不会出现G_IO_ERROR_EXISTS错误值, * 所以只会弹出错误提示对话框,给用户提示,结束还原操作流程,避免后续操作产生错误。 */ FileOperationError except; untrashFileErrDlg(except, srcFile, destFile, err); g_error_free(err); } return ret; } int FileUntrashOperation::moveRecursively(FileNode *fileNode, QString &destPath) { int ret = 0; QString srcFile = fileNode->uri(); if (fileNode->isFolder()) { if (!FileUtils::isFileExsit(destPath)){ //如果目录不存在,则创建 GError *err = nullptr; auto originFile = wrapGFile(g_file_new_for_uri(FileUtils::urlEncode(destPath).toUtf8().constData())); g_file_make_directory(originFile.get()->get(), getCancellable().get()->get(), &err); if (err) { ret = -err->code; qWarning()<< "make dir:" << srcFile << " Error info:" << err->message << " Error code:" << ret; /* * 这里是对不存在的目录进行创建,不会出现G_IO_ERROR_EXISTS错误值, * 所以只会弹出错误提示对话框,给用户提示,结束还原操作流程, 避免后续操作产生错误。 */ FileOperationError except; untrashFileErrDlg(except, srcFile, destPath, err); g_error_free(err); return ret; } } for (auto child : *(fileNode->children())) { int ret = moveRecursively(child, destPath); if (ret < 0) { return ret; } } } else { int ret = copyFileProcess(srcFile, destPath); if (ret < 0) { return ret; } } return 0; } int FileUntrashOperation::deleteFileProcess(FileNode *fileNode) { int ret = 0; GError *err = nullptr; QString destFile = nullptr; QString srcFile = fileNode->uri(); GFile *file = g_file_new_for_uri(FileUtils::urlEncode(srcFile).toUtf8().constData()); g_file_delete(file, getCancellable().get()->get(), &err); if (err){ ret = -err->code; qWarning()<< "delete file:" << srcFile << " Error info:" << err->message << " Error code:" << ret; FileOperationError except; untrashFileErrDlg(except, srcFile, destFile, err); g_error_free(err); } return ret; } int FileUntrashOperation::untrashFileOverWrite(QString &uri) { int ret = 0; //1、通过树形结构,构建目录下的节点(文件和目录) FileNode *node = new FileNode(uri, nullptr, nullptr); if (node->isFolder()) { node->findChildrenRecursively(); QString originParentPath = m_restore_hash.value(uri) ; //2、对node的树形结构进行递归遍历处理 for (auto child : *(node->children())) { QString destPath = originParentPath + '/' + child->baseName(); ret = moveRecursively(child, destPath); if (ret < 0) { goto l_free; } } } else { QString originParentPath = m_restore_hash.value(uri); ret = moveRecursively(node, originParentPath); if (ret < 0) { goto l_free; } } //3、删除回收站中的目录 ret = deleteFileProcess(node); //4、释放filenode空间 l_free: delete node; return ret; } void FileUntrashOperation::run() { /*! \bug can not restore the files in desktop. it caused by the parent uri string has chinese. */ int ret = 0; for (auto uri : m_uris) { //cacheOriginalUri(); auto originUri = m_restore_hash.value(uri); if (originUri.isEmpty()) { // try get meta info origin path FileInfoJob j(uri); j.querySync(); auto trashedFileLocaledUri = FileUtils::getTargetUri(uri); FileInfoJob j2(trashedFileLocaledUri); j2.querySync(); auto metaInfo = FileMetaInfo::fromUri(trashedFileLocaledUri); if (metaInfo) { // there is a case which makes peony crash. // 1. trash an item in desktop application. // 2. restore the item from peony application. // 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 happend. originUri = "file://" + metaInfo.get()->getMetaInfoString("orig-path"); } else { qWarning()<<"invalid file meta info orig-path"<get(), destFile.get()->get(), GFileCopyFlags(m_default_copy_flag), getCancellable().get()->get(), nullptr, nullptr, &err); } if (err) { FileOperationError except; ExceptionResponse type = prehandle(err); if (Other == type) { untrashFileErrDlg(except, uri, originUri, err); type = except.respCode; } g_error_free(err); err = nullptr; switch (type) { case Retry: goto retry; case Cancel: cancel(); break; case OverWriteOne: ret = untrashFileOverWrite(uri); if (ret < 0){ goto l_out; } break; case OverWriteAll: ret = untrashFileOverWrite(uri); if (ret < 0){ goto l_out; } m_prehandle_hash.insert(except.errorCode, OverWriteOne); break; case BackupOne: { // use custom name getBackupName(originUri, except); if (FileUtils::isFileExsit(originUri)) { originUri = handleDuplicate(originUri); } destFile = wrapGFile(g_file_new_for_uri(originUri.toUtf8().constData())); goto retry; } case BackupAll: { originUri = handleDuplicate(originUri); destFile = wrapGFile(g_file_new_for_uri(originUri.toUtf8().constData())); m_prehandle_hash.insert(except.errorCode, BackupOne); goto retry; } case IgnoreOne: { break; } case IgnoreAll: { m_prehandle_hash.insert(except.errorCode, IgnoreOne); break; } default: break; } } } l_out: operationFinished(); } peony/libpeony-qt/file-operation/file-count-operation.h0000644000175000017500000000350414205101223022214 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILECOUNTOPERATION_H #define FILECOUNTOPERATION_H #include "file-operation.h" namespace Peony { class FileNodeReporter; class FileCountOperation : public FileOperation { Q_OBJECT public: explicit FileCountOperation(const QStringList &uris, bool countRoot = true, QObject *parent = nullptr); ~FileCountOperation() override; void run() override; std::shared_ptr getOperationInfo() override { return nullptr; } void getInfo(quint64 &file_count, quint64 &hidden_file_count, quint64 &total_size) { file_count = m_file_count; hidden_file_count = m_hidden_file_count; total_size = m_total_size; } Q_SIGNALS: void countDone(quint64 file_count, quint64 hidden_file_count, quint64 total_size); public Q_SLOTS: void cancel() override; private: FileNodeReporter *m_reporter = nullptr; QStringList m_uris; quint64 m_file_count = 0; quint64 m_hidden_file_count = 0; quint64 m_total_size = 0; bool m_count_root = true; }; } #endif // FILECOUNTOPERATION_H peony/libpeony-qt/file-operation/file-operation-test/0000755000175000017500000000000014205101223021670 5ustar fengfengpeony/libpeony-qt/file-operation/file-operation-test/mainwindow.h0000644000175000017500000000235414205101223024221 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include "file-operation.h" #include "gerror-wrapper.h" class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); ~MainWindow(); public Q_SLOTS: QVariant handleError(const QString &srcUri, const QString &destDirUri, const Peony::GErrorWrapperPtr &err); }; #endif // MAINWINDOW_H peony/libpeony-qt/file-operation/file-operation-test/file-operation-test.pro0000644000175000017500000000243014205101223026303 0ustar fengfeng#------------------------------------------------- # # Project created by QtCreator 2019-08-05T10:27:03 # #------------------------------------------------- QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = file-operation-test TEMPLATE = app # The following define makes your compiler emit warnings if you use # any feature of Qt which has been marked as 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 you use 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 CONFIG += link_pkgconfig no_keywords c++11 PKGCONFIG += glib-2.0 gio-2.0 include(../file-operation.pri) include(../../peony-core.pri) SOURCES += \ main.cpp \ mainwindow.cpp HEADERS += \ mainwindow.h # Default rules for deployment. #qnx: target.path = /tmp/$${TARGET}/bin #else: unix:!android: target.path = /opt/$${TARGET}/bin #!isEmpty(target.path): INSTALLS += target peony/libpeony-qt/file-operation/file-operation-test/mainwindow.cpp0000644000175000017500000001600014205101223024545 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #include "mainwindow.h" #include "file-move-operation.h" #include "file-node.h" #include "file-node-reporter.h" #include "connect-server-dialog.h" #include "gerror-wrapper.h" #include "file-operation/file-operation-progress-wizard.h" #include "file-operation-error-dialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "file-copy-operation.h" #include "file-delete-operation.h" #include "file-link-operation.h" #include "file-trash-operation.h" #include "file-untrash-operation.h" #include "file-rename-operation.h" #include "file-enumerator.h" #include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { qDebug()<setOrientation(Qt::Vertical); t->addWidget(current_uri_label); t->addWidget(offset_label); QAction *startAction = new QAction("start", t); t->addAction(startAction); connect(startAction, &QAction::triggered, [=] { QMessageBox::question(nullptr, tr("source files"), tr("choose source files," "use cancel to finish the choices.")); QFileDialog srcdlg; srcdlg.setFileMode(QFileDialog::ExistingFiles); srcdlg.setAcceptMode(QFileDialog::AcceptSave); srcdlg.setViewMode(QFileDialog::List); QStringList srcUris; srcdlg.exec(); for (auto uri : srcdlg.selectedUrls()) { srcUris<setForceUseFallback(); //copy //Peony::FileCopyOperation *moveOp = new Peony::FileCopyOperation(srcUris, destUri); //delete //Peony::FileDeleteOperation *moveOp = new Peony::FileDeleteOperation(srcUris); //link //Peony::FileLinkOperation *moveOp = new Peony::FileLinkOperation(srcUris.isEmpty()? nullptr: srcUris.at(0), destUri); //trash //Peony::FileTrashOperation *moveOp = new Peony::FileTrashOperation(srcUris); //untrash /* Peony::FileEnumerator e; e.setEnumerateDirectory("trash:///"); e.enumerateSync(); auto infos = e.getChildren(); QStringList uris; for (auto info : infos) { uris<uri(); } Peony::FileUntrashOperation *moveOp = new Peony::FileUntrashOperation(uris); */ //rename //Peony::FileRenameOperation *moveOp = new Peony::FileRenameOperation(srcUris.isEmpty()? nullptr: srcUris.at(0), "RenameSample"); moveOp->connect(moveOp, &Peony::FileOperation::errored, this, &MainWindow::handleError, Qt::BlockingQueuedConnection); /* moveOp->connect(moveOp, &Peony::FileOperation::invalidOperation, [=](const QString &message){ QMessageBox::critical(nullptr, "Error", message); }); */ Peony::FileOperationProgressWizard *wizard = new Peony::FileOperationProgressWizard; wizard->connect(moveOp, &Peony::FileOperation::operationStarted, wizard, &Peony::FileOperationProgressWizard::show, Qt::BlockingQueuedConnection); wizard->connect(moveOp, &Peony::FileOperation::operationPreparedOne, wizard, &Peony::FileOperationProgressWizard::onElementFoundOne); wizard->connect(moveOp, &Peony::FileOperation::operationPrepared, wizard, &Peony::FileOperationProgressWizard::switchToProgressPage); wizard->connect(moveOp, &Peony::FileOperation::operationProgressedOne, wizard, &Peony::FileOperationProgressWizard::onFileOperationProgressedOne); //operationFinished has a few time delay because there are many resources need be released and deconstructor. wizard->connect(moveOp, &Peony::FileOperation::operationProgressed, wizard, &Peony::FileOperationProgressWizard::onFileOperationProgressedAll); wizard->connect(moveOp, &Peony::FileOperation::operationAfterProgressedOne, wizard, &Peony::FileOperationProgressWizard::onElementClearOne); connect(wizard, &Peony::FileOperationProgressWizard::cancelled, moveOp, &Peony::FileOperation::cancel); connect(moveOp, &Peony::FileOperation::operationStartRollbacked, wizard, &Peony::FileOperationProgressWizard::switchToRollbackPage); connect(moveOp, &Peony::FileOperation::operationRollbackedOne, wizard, &Peony::FileOperationProgressWizard::onFileRollbacked); wizard->connect(moveOp, &Peony::FileOperation::operationFinished, wizard, &QDialog::accepted); connect(wizard, &QDialog::accepted, wizard, &Peony::FileOperationProgressWizard::deleteLater); QThreadPool::globalInstance()->start(moveOp); }); } MainWindow::~MainWindow() { } QVariant MainWindow::handleError(const QString &srcUri, const QString &destDirUri, const Peony::GErrorWrapperPtr &err) { Peony::FileOperationErrorDialog dlg; if (err.get()->code() == G_IO_ERROR_INVALID_FILENAME) { QMessageBox::critical(nullptr, "Critical:", err.get()->message()); return QVariant(Peony::FileOperation::ResponseType::IgnoreAll); } return dlg.handleError(srcUri, destDirUri, err); } peony/libpeony-qt/file-operation/file-operation-test/main.cpp0000644000175000017500000000234014205101223023317 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #include "mainwindow.h" #include #include #include "gerror-wrapper.h" #include int main(int argc, char *argv[]) { QApplication a(argc, argv); QIcon::setThemeName("ukui-icon-theme"); qRegisterMetaType("Peony::GErrorWrapperPtr"); qRegisterMetaType("Peony::GErrorWrapperPtr&"); MainWindow w; w.show(); return a.exec(); } peony/libpeony-qt/file-operation/file-untrash-operation.h0000644000175000017500000000451514205101223022553 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILEUNTRASHOPERATION_H #define FILEUNTRASHOPERATION_H #include "peony-core_global.h" #include "file-operation.h" #include "file-node.h" namespace Peony { /*! * \brief The FileUntrashOperation class * \bug * can not restore the files which's parents has chinese. */ class PEONYCORESHARED_EXPORT FileUntrashOperation : public FileOperation { Q_OBJECT public: explicit FileUntrashOperation(QStringList uris, QObject *parent = nullptr); void run() override; std::shared_ptr getOperationInfo() override { return m_info; } protected: void cacheOriginalUri(); const QString handleDuplicate(const QString &uri); private: ExceptionResponse prehandle(GError *err); void getBackupName(QString &originUri, FileOperationError &except); void untrashFileErrDlg( FileOperationError &except, QString &srcUri, QString &originUri, GError *err); int untrashFileOverWrite(QString &uri); int moveRecursively(FileNode *fileNode, QString &parentPath); int copyFileProcess(QString &srcFile, QString &destFile); int deleteFileProcess(FileNode *fileNode); GFileCopyFlags m_default_copy_flag = GFileCopyFlags(G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA); QStringList m_uris; QHash m_restore_hash; ExceptionResponse m_pre_handler = Invalid; QHash m_prehandle_hash; std::shared_ptr m_info = nullptr; }; } #endif // FILEUNTRASHOPERATION_H peony/libpeony-qt/file-operation/create-template-operation.h0000644000175000017500000000330114205101223023216 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef CREATETEMPLATEOPERATION_H #define CREATETEMPLATEOPERATION_H #include #include "peony-core_global.h" #include "file-operation.h" namespace Peony { class PEONYCORESHARED_EXPORT CreateTemplateOperation : public FileOperation { Q_OBJECT public: enum Type { EmptyFile, EmptyFolder, Template }; explicit CreateTemplateOperation(const QString &destDirUri, Type type = EmptyFile, const QString &templateName = nullptr, QObject *parent = nullptr); void run() override; std::shared_ptr getOperationInfo() override { return m_info; } const QString target() { return m_target_uri; } protected: void handleDuplicate(const QString &uri); private: std::shared_ptr m_info; QString m_src_uri; QString m_dest_dir_uri; QString m_target_uri; Type m_type; }; } #endif // CREATETEMPLATEOPERATION_H peony/libpeony-qt/file-operation/file-delete-operation.cpp0000644000175000017500000001175614205115226022701 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-delete-operation.h" #include "file-operation-manager.h" #include "file-node.h" #include "file-node-reporter.h" using namespace Peony; FileDeleteOperation::FileDeleteOperation(QStringList sourceUris, QObject *parent) : FileOperation(parent) { m_source_uris = sourceUris; m_reporter = new FileNodeReporter; m_info = std::make_shared(sourceUris, nullptr, FileOperationInfo::Delete); connect(m_reporter, &FileNodeReporter::nodeFound, this, &FileOperation::operationPreparedOne); } FileDeleteOperation::~FileDeleteOperation() { delete m_reporter; } std::shared_ptr FileDeleteOperation::getOperationInfo() { return m_info; } void FileDeleteOperation::deleteRecursively(FileNode *node) { if (isCancelled()) return; auto fileIconName = FileUtils::getFileIconName(FileUtils::urlEncode(node->uri()), false); GFile *file = g_file_new_for_uri(FileUtils::urlEncode(node->uri()).toUtf8().constData()); if (node->isFolder()) { for (auto child : *(node->children())) { deleteRecursively(child); } GError *err = nullptr; g_file_delete(file, getCancellable().get()->get(), &err); if (err) { if (!m_prehandle_hash.isEmpty()) { g_error_free(err); return; } // if delete a file get into error, it might be a critical error. FileOperationError except; except.errorType = ET_GIO; except.dlgType = ED_WARNING; except.srcUri = node->uri(); except.op = FileOpDelete; except.title = tr("File delete error"); except.errorStr = err->message; except.errorCode = err->code; Q_EMIT errored(except); auto response = except.respCode; auto responseType = response; if (responseType == Cancel) { cancel(); } // Similar errors only remind the user once m_prehandle_hash.insert(err->code, IgnoreAll); } } else { GError *err = nullptr; g_file_delete(file,getCancellable().get()->get(),&err); if (err) { if (!m_prehandle_hash.isEmpty()) { g_error_free(err); return; } // if delete a file get into error, it might be a critical error. FileOperationError except; except.errorType = ET_GIO; except.dlgType = ED_WARNING; except.srcUri = node->uri(); except.op = FileOpDelete; except.title = tr("File delete error"); except.errorCode = err->code; except.errorStr = err->message; Q_EMIT errored(except); auto response = except.respCode; qDebug()<code, IgnoreAll); } } g_object_unref(file); qDebug()<<"deleted"; //operationAfterProgressedOne(node->uri()); m_current_offset += node->size(); FileProgressCallback(node->uri(), node->uri(), fileIconName, m_current_offset, m_total_szie); } void FileDeleteOperation::run() { if (isCancelled()) return; Q_EMIT operationStarted(); Q_EMIT operationRequestShowWizard(); goffset *total_size = new goffset(0); QList nodes; for (auto uri : m_source_uris) { FileNode *node = new FileNode(FileUtils::urlEncode(uri), nullptr, m_reporter); node->findChildrenRecursively(); node->computeTotalSize(total_size); nodes<cancel(); FileOperation::cancel(); } peony/libpeony-qt/file-operation/file-operation-error-dialog.cpp0000644000175000017500000001357514205101223024016 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-operation-error-dialog.h" #include #include #include #include #include #include #include #include using namespace Peony; FileOperationErrorDialog::FileOperationErrorDialog(QWidget *parent) : QDialog(parent) { //center to desktop. setParent(QApplication::desktop()); //setWindowFlag(Qt::Dialog); //use WindowStaysOnTopHint flag to make sure this dialog always stay on top setWindowFlags(Qt::Dialog | Qt::WindowStaysOnTopHint); setWindowTitle(tr("File Operation Error")); setWindowIcon(QIcon::fromTheme("system-error")); m_layout = new QFormLayout(this); m_layout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint); m_layout->setRowWrapPolicy(QFormLayout::WrapAllRows); m_layout->setLabelAlignment(Qt::AlignRight); m_layout->setFormAlignment(Qt::AlignLeft); m_err_line = new QLabel(tr("unkwon"), this); m_src_line = new QLabel(tr("null"), this); m_dest_line = new QLabel(tr("null"), this); m_layout->addRow(tr("Error message:"), m_err_line); m_layout->addRow(tr("Source File:"), m_src_line); m_layout->addRow(tr("Dest File:"), m_dest_line); m_button_box = new QDialogButtonBox(this); m_button_box2 = new QDialogButtonBox(this); QPushButton *ignoreOneBt = new QPushButton(tr("Ignore"), m_button_box); QPushButton *ignoreAllBt = new QPushButton(tr("Ignore All"), m_button_box); QPushButton *overwriteOneBt = new QPushButton(tr("Overwrite"), m_button_box); QPushButton *overwriteAllBt = new QPushButton(tr("Overwrite All"), m_button_box); QPushButton *backupOneBt = new QPushButton(tr("Backup"), m_button_box); QPushButton *backupAllBt = new QPushButton(tr("Backup All"), m_button_box); QPushButton *retryBt = new QPushButton(tr("&Retry"), m_button_box); QPushButton *cancelBt = new QPushButton(tr("&Cancel"), m_button_box); btGroup = new QButtonGroup(this); btGroup->addButton(ignoreOneBt, 1); btGroup->addButton(ignoreAllBt, 2); btGroup->addButton(overwriteOneBt, 3); btGroup->addButton(overwriteAllBt, 4); btGroup->addButton(backupOneBt, 5); btGroup->addButton(backupAllBt, 6); btGroup->addButton(retryBt, 7); btGroup->addButton(cancelBt, 8); connect(btGroup, SIGNAL(buttonClicked(int)), this, SLOT(done(int))); m_button_box->addButton(ignoreOneBt, QDialogButtonBox::ActionRole); m_button_box->addButton(ignoreAllBt, QDialogButtonBox::ActionRole); m_button_box->addButton(overwriteOneBt, QDialogButtonBox::ActionRole); m_button_box->addButton(overwriteAllBt, QDialogButtonBox::ActionRole); m_button_box2->addButton(backupOneBt, QDialogButtonBox::ActionRole); m_button_box2->addButton(backupAllBt, QDialogButtonBox::ActionRole); m_button_box2->addButton(retryBt, QDialogButtonBox::ActionRole); m_button_box2->addButton(cancelBt, QDialogButtonBox::ActionRole); m_layout->addWidget(m_button_box); m_layout->addWidget(m_button_box2); setLayout(m_layout); } FileOperationErrorDialog::~FileOperationErrorDialog() { } FileOperationErrorHandler::~FileOperationErrorHandler() { } void FileOperationErrorDialog::handle (FileOperationError& error) { Q_UNUSED(error); } int FileOperationErrorDialog::handleError(const QString &srcUri, const QString &destDirUri, const GErrorWrapperPtr &err, bool isCritical) { QUrl srcUrl = srcUri; QUrl destDirUrl = destDirUri; for (int i = 3; i < 8; i++) { btGroup->button(i)->setVisible(!isCritical); } pfontMetrics = new QFontMetrics(m_src_line->font()); int charWidth = pfontMetrics->averageCharWidth(); QString edit_srcUri = pfontMetrics->elidedText(srcUrl.toDisplayString(), Qt::ElideRight, ELIDE_ERROR_TEXT_LENGTH * charWidth); QString edit_destDirUri = pfontMetrics->elidedText(destDirUrl.toDisplayString(), Qt::ElideRight, ELIDE_ERROR_TEXT_LENGTH * charWidth); QString edit_err_text = pfontMetrics->elidedText(err.get()->message(), Qt::ElideRight, ELIDE_ERROR_TEXT_LENGTH * charWidth); delete pfontMetrics; pfontMetrics = nullptr; m_src_line->setText(edit_srcUri); m_dest_line->setText(edit_destDirUri); m_err_line->setText(edit_err_text); //FIXME: how to uniform the button with dynamically? //the default size won't change before the dialog has shown. //do i need compute the longest text width? but the text is 18in... for (int i = 1; i < 9; i++) { btGroup->button(i)->setFixedWidth(100); } int val = exec(); switch (val) { case 1: { return ExceptionResponse::IgnoreOne; } case 2: { return ExceptionResponse::IgnoreAll; } case 3: { return ExceptionResponse::OverWriteOne; } case 4: { return ExceptionResponse::OverWriteAll; } case 5: { return ExceptionResponse::BackupOne; } case 6: { return ExceptionResponse::BackupAll; } case 7: { return ExceptionResponse::Retry; } case 8: { return ExceptionResponse::Cancel; } default: { return ExceptionResponse::IgnoreAll; } } } peony/libpeony-qt/file-operation/file-operation.cpp0000644000175000017500000001204114205115226021425 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include #include #include #include #include #include "file-operation.h" #include "file-operation-manager.h" using namespace Peony; QRegExp gInvalidName("[\\\\/:\\*\\?\\\"<>\\|]");/* 文件名或文件夹名中不能出现以下字符:\、/、:、*、?、"、<、>、| */ FileOperation::FileOperation(QObject *parent) : QObject (parent) { m_cancellable_wrapper = wrapGCancellable(g_cancellable_new()); setAutoDelete(true); connect(this, &FileOperation::operationPause, this, [=] () { m_is_pause = true; }); connect(this, &FileOperation::operationResume, this, [=] () { m_is_pause = false; }); } FileOperation::~FileOperation() { } void FileOperation::run() { } void FileOperation::setHasError(bool hasError) { m_has_error = hasError; getOperationInfo().get()->m_has_error = hasError; } void FileOperation::cancel() { g_cancellable_cancel(m_cancellable_wrapper.get()->get()); m_is_cancelled = true; } bool FileOperation::nameIsValid (QString& name) { if (nullptr == name) { return false; } return !name.contains(gInvalidName); } bool FileOperation::makeFileNameValidForDestFS(QString &srcPath, QString &destPath, QString *newFileName) { FileInfoJob fileInfoJob(destPath); FileInfoJob fileInfoJobSrc(srcPath); fileInfoJob.querySync(); fileInfoJobSrc.querySync(); QString srcFileName = fileInfoJobSrc.getInfo()->displayName(); *newFileName = srcFileName; QString fsType = fileInfoJob.getInfo()->fileSystemType(); qDebug() << "target filesystem type is: " << fsType; if ("fat" == fsType || "vfat" == fsType || "fuse" == fsType || "ntfs" == fsType || "msdos" == fsType || "msdosfs" == fsType) { *newFileName = (*newFileName).replace(gInvalidName, "_"); qDebug() << "uri:" << QUrl(srcPath).toDisplayString() << "target filesystem type is: " << fsType << " old name:" << srcFileName << " new name:" << *newFileName; } return *newFileName != srcFileName; } void FileOperation::fileSync(QString srcFile, QString destDir) { if (srcFile.endsWith("/")) { srcFile.chop(1); } QString destFile = ""; if (destDir.split("/").back() == srcFile.split("/").back()) { destFile = destDir; } else { destFile = destDir + "/" + srcFile.split("/").back(); } bool needSync = true; // if (!FileUtils::isFileExsit(destFile)) { // qDebug() << "file:" << destFile << " is not existed!"; // return; // } // bool needSync = false; GFile* srcGfile = g_file_new_for_uri(srcFile.toUtf8().constData()); GFile* destGfile = g_file_new_for_uri(destFile.toUtf8().constData()); GMount* srcMount = g_file_find_enclosing_mount(srcGfile, nullptr, nullptr); GMount* destMount = g_file_find_enclosing_mount(destGfile, nullptr, nullptr); // xxxMount is null in root filesystem if ((srcMount != destMount) && (NULL != destMount)) { needSync = true; } if (needSync) { Q_EMIT operationStartSnyc(); auto path = g_file_get_path(destGfile); qDebug() << "DJ- sync -- src: " << srcFile << " === " << destDir << " === " << destFile << " path:" << path; if (path) { QProcess p; auto shellPath = g_shell_quote(path); qDebug() << "DJ- start execute: " << QString("sync -f %1").arg(shellPath); p.start(QString("sync -f %1").arg(shellPath)); qDebug() << "DJ- execute: " << QString("sync -f %1 ok!!!").arg(shellPath); g_free(path); g_free(shellPath); p.waitForFinished(-1); } } if (nullptr != srcMount) { g_object_unref(srcMount); } if (nullptr != destMount) { g_object_unref(destMount); } if (nullptr != srcGfile) { g_object_unref(srcGfile); } if (nullptr != destGfile) { g_object_unref(destGfile); } } void FileOperation::notifyFileWatcherOperationFinished() { if (!qApp->allWidgets().isEmpty()) { // notify operation for file watchers. auto info = this->getOperationInfo(); qDebug()<m_src_dir_uri; if (info) { FileOperationManager::getInstance()->manuallyNotifyDirectoryChanged(info.get()); } } } peony/libpeony-qt/file-operation/file-operation-progress-wizard.cpp0000644000175000017500000002706114205101223024565 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-operation-progress-wizard.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Peony; FileOperationProgressWizard::FileOperationProgressWizard(QWidget *parent) : QWizard(parent) { setFixedWidth(600); setFixedHeight(480); setWindowFlags(windowFlags()); setWindowTitle(tr("File Manager")); //only show cancel button at bottom-right of wizard QList layout; layout<cancelled(); }); setButtonLayout(layout); //connect(this, &QDialog::rejected, this, &FileOperationProgressWizard::cancelled); m_first_page = new FileOperationPreparePage(this); m_first_page->setTitle(tr("Preparing...")); addPage(m_first_page); m_second_page = new FileOperationProgressPage(this); m_second_page->setTitle(tr("Handling...")); addPage(m_second_page); m_third_page = new FileOperationAfterProgressPage(this); m_third_page->setTitle(tr("Clearing...")); addPage(m_third_page); m_last_page = new FileOperationRollbackPage(this); m_last_page->setTitle(tr("Rollbacking...")); addPage(m_last_page); m_tray_icon = new QSystemTrayIcon(QIcon::fromTheme("system-file-manager"), this); m_tray_icon->setToolTip(tr("File Operation")); connect(m_tray_icon, &QSystemTrayIcon::activated, [=]() { this->show(); m_tray_icon->hide(); }); m_delayer = new QTimer(this); m_tip_delayer = new QTimer(this); m_delayer->setSingleShot(true); m_delayer->setInterval(100); connect(m_tip_delayer, &QTimer::timeout, [=]() { m_tray_icon->showMessage(tr("File Operation"), tr("A file operation is running backend..."), #if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) QIcon::fromTheme("system-file-manager"), #else QSystemTrayIcon::MessageIcon::Information, #endif 5000); m_tip_delayer->stop(); }); } FileOperationProgressWizard::~FileOperationProgressWizard() { } void FileOperationProgressWizard::closeEvent(QCloseEvent *e) { //NOTE: the wizard will destroy when file operation finished. //ignore the close event and just hide itself. //FIXME: the close button bolder-style will changed if closeEvent //was overwrite. is that a bug? e->ignore(); m_tray_icon->show(); m_tray_icon->showMessage(tr("File Operation"), tr("A file operation is running backend..."), #if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) QIcon::fromTheme("system-file-manager"), #else QSystemTrayIcon::MessageIcon::Information, #endif 1); m_tip_delayer->start(800); hide(); } void FileOperationProgressWizard::delayShow() { QTimer::singleShot(1000, this, &FileOperationProgressWizard::show); } void FileOperationProgressWizard::switchToPreparedPage() { restart(); m_total_count = 0; m_total_size = 0; auto cancelButton = button(QWizard::CustomButton1); cancelButton->setEnabled(true); } void FileOperationProgressWizard::onElementFoundOne(const QString &uri, const qint64 &size) { qDebug()<<"onElementFound"<m_src_line->setText(uri); m_first_page->m_state_line->setText(tr("%1 files, %2").arg(m_total_count).arg(format_size)); g_free(format_size); } void FileOperationProgressWizard::onElementFoundAll() { switchToProgressPage(); } void FileOperationProgressWizard::switchToProgressPage() { restart(); next(); auto cancelButton = button(QWizard::CustomButton1); cancelButton->setEnabled(true); } void FileOperationProgressWizard::onFileOperationProgressedOne(const QString &uri, const QString &destUri, const qint64 &size) { m_current_count++; return; } void FileOperationProgressWizard::onFileOperationProgressedAll() { switchToAfterProgressPage(); } void FileOperationProgressWizard::switchToAfterProgressPage() { restart(); next(); next(); auto cancelButton = button(QWizard::CustomButton1); cancelButton->setEnabled(true); } void FileOperationProgressWizard::onElementClearOne(const QString &uri) { m_third_page->m_file_deleted_count++; m_third_page->m_src_line->setText(tr("clearing: %1, %2 of %3").arg(uri). arg(m_third_page->m_file_deleted_count). arg(m_total_count)); if (m_total_count > 0) m_third_page->m_progress_bar->setValue(int(m_third_page->m_file_deleted_count*100.0)/m_total_count); } void FileOperationProgressWizard::switchToRollbackPage() { restart(); next(); next(); next(); //rollback is not cancellable, so disable cancel button. auto cancelButton = button(QWizard::CustomButton1); cancelButton->setEnabled(false); } void FileOperationProgressWizard::onFileRollbacked(const QString &destUri, const QString &srcUri) { Q_UNUSED(destUri); Q_UNUSED(srcUri); m_last_page->m_current_count++; auto c = m_last_page->m_current_count; auto t = m_current_count; auto v = qreal(c*1.0/t)*100; //use wizard's m_current_count as total count of files need rollback. m_last_page->m_progress_bar->setValue(int(v)); } void FileOperationProgressWizard::updateProgress(const QString &srcUri, const QString &destUri, quint64 current, quint64 total) { if (m_delayer->isActive()) { return; } if (current > m_total_size) return; m_delayer->start(); if (m_second_page->m_state_line->text() == "unknown") { m_second_page->m_state_line->setText(tr("copying...")); } m_second_page->m_src_line->setText(srcUri); m_second_page->m_dest_line->setText(destUri); //char *current_format_size = g_format_size (quint64(current)); //char *total_format_size = g_format_size(quint64(m_total_size)); //Calculated by 1024 bytes char *current_format_size = strtok(g_format_size_full(quint64(current),G_FORMAT_SIZE_IEC_UNITS),"iB"); char *total_format_size = strtok(g_format_size_full(quint64(m_total_size),G_FORMAT_SIZE_IEC_UNITS),"iB"); m_second_page->m_state_line->setText(tr("%1 done, %2 total, %3 of %4."). arg(current_format_size).arg(total_format_size) .arg(m_current_count).arg(m_total_count)); g_free(current_format_size); g_free(total_format_size); m_second_page->m_src_line->setText(srcUri); m_second_page->m_dest_line->setText(destUri); double test = (m_current_size*1.0/m_total_size)*100; m_second_page->m_progress_bar->setValue(int(test)); double progress = current*1.0/total; m_second_page->m_progress_bar->setValue(int(progress*100)); } void FileOperationProgressWizard::onStartSync() { switchToAfterProgressPage(); m_third_page->setTitle(tr("Syncing...")); m_third_page->m_src_line->hide(); // set a busy progress bar m_third_page->m_progress_bar->setMaximum(0); m_third_page->m_progress_bar->setMinimum(0); //QMessageBox::information(0, 0, "syncing"); } //FileOperationPreparePage FileOperationPreparePage::FileOperationPreparePage(QWidget *parent) : QWizardPage (parent) { m_layout = new QFormLayout(this); m_layout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint); m_layout->setLabelAlignment(Qt::AlignRight); m_layout->setFormAlignment(Qt::AlignLeft); m_src_line = new QLabel("null", this); m_state_line = new QLabel("0 files, 0 bytes", this); m_layout->addRow(tr("counting:"), m_src_line); m_layout->addRow(tr("state:"), m_state_line); setLayout(m_layout); } FileOperationPreparePage::~FileOperationPreparePage() { } //FileOperationProgressPage FileOperationProgressPage::FileOperationProgressPage(QWidget *parent) : QWizardPage (parent) { m_layout = new QGridLayout(this); m_state_line = new QLabel("unknown", this); m_progress_bar = new QProgressBar(this); m_layout->addWidget(m_state_line, 0, 0); m_layout->addWidget(m_progress_bar, 1, 0); QPushButton *details_button = new QPushButton(tr("&More Details"), this); details_button->setCheckable(true); m_layout->addWidget(details_button, 2, 0, Qt::AlignRight); QFormLayout *moreDetailsLayout = new QFormLayout; moreDetailsLayout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint); moreDetailsLayout->setLabelAlignment(Qt::AlignRight); moreDetailsLayout->setFormAlignment(Qt::AlignLeft); m_src_line = new QLabel("null", this); m_dest_line = new QLabel("null", this); moreDetailsLayout->addRow(tr("From:"), m_src_line); moreDetailsLayout->addRow(tr("To:"), m_dest_line); QWidget *detailsWidget = new QWidget(this); detailsWidget->setLayout(moreDetailsLayout); m_layout->addWidget(detailsWidget, 3, 0); detailsWidget->hide(); connect(details_button, &QAbstractButton::toggled, detailsWidget, &QWidget::setVisible); setLayout(m_layout); } FileOperationProgressPage::~FileOperationProgressPage() { } //FileOperationAfterProgressPage FileOperationAfterProgressPage::FileOperationAfterProgressPage(QWidget *parent) : QWizardPage (parent) { m_layout = new QGridLayout(this); m_src_line = new QLabel("clearing: null", this); //avoid wizard size hint changed. m_src_line->setWordWrap(true); m_src_line->setVisible(false); m_progress_bar = new QProgressBar(this); m_layout->addWidget(m_progress_bar, 0, 0); QPushButton *details_button = new QPushButton(tr("&More Details"), this); details_button->setCheckable(true); m_layout->addWidget(details_button, 1, 0, Qt::AlignRight); m_layout->addWidget(m_src_line, 2, 0); details_button->setVisible(false); m_src_line->hide(); connect(details_button, &QAbstractButton::toggled, m_src_line, &QLabel::setVisible); setLayout(m_layout); } FileOperationAfterProgressPage::~FileOperationAfterProgressPage() { } //FileOperationRollbackPage FileOperationRollbackPage::FileOperationRollbackPage(QWidget *parent) : QWizardPage (parent) { m_layout = new QGridLayout(this); m_progress_bar = new QProgressBar(this); m_layout->addWidget(m_progress_bar, 0, 0); setLayout(m_layout); } FileOperationRollbackPage::~FileOperationRollbackPage() { } peony/libpeony-qt/file-operation/file-operation-error-dialog-base.h0000644000175000017500000000371414205115226024375 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Jing Ding * */ #ifndef FILEOPERATIONERRORDIALOGBASE_H #define FILEOPERATIONERRORDIALOGBASE_H #include #include #include "file-operation-error-handler.h" namespace Peony { class PEONYCORESHARED_EXPORT FileOperationErrorDialogBase : public QDialog, public FileOperationErrorHandler { Q_OBJECT Q_INTERFACES(Peony::FileOperationErrorHandler) public: explicit FileOperationErrorDialogBase(QDialog *parent); ~FileOperationErrorDialogBase() override; void setHeaderIcon (QString icon); Q_SIGNALS: void cancel(); protected: void paintEvent(QPaintEvent*) override; void mouseMoveEvent(QMouseEvent *event)override; void mousePressEvent(QMouseEvent *event)override; protected: FileOperationError* m_error = nullptr; private: float m_btn_size = 20; float m_margin_tp = 9; float m_margin_lr = 16; float m_btn_margin = 5; float m_header_height = 30; float m_header_icon_size = 20; QString m_header_icon; }; }; #endif // FILEOPERATIONERRORDIALOGBASE_H peony/libpeony-qt/file-operation/file-node.cpp0000644000175000017500000001001214205115226020346 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-node.h" #include "file-utils.h" #include "file-info.h" #include "file-node-reporter.h" using namespace Peony; FileNode::FileNode(QString uri, FileNode *parent, FileNodeReporter *reporter) { m_uri = uri; m_parent = parent; m_reporter = reporter; GFile *file = g_file_new_for_uri(uri.toUtf8().constData()); char *basename = g_file_get_basename(file); m_basename = basename; m_dest_basename = basename; m_basename = m_uri.split("/").last(); m_dest_basename = m_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; GFileInfo *info = g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); g_object_unref(file); m_size = g_file_info_get_size(info); if (uri == "file:///proc/kcore") m_size = 0; g_object_unref(info); if (m_reporter) { m_reporter->sendNodeFound(m_uri, m_size); } m_children = new QList(); } FileNode::~FileNode() { qDebug()<<"delete node:"<clear(); delete m_children; } void FileNode::findChildrenRecursively() { if (m_reporter) { if (m_reporter->isOperationCancelled()) return; } if (!m_is_folder) return; else { auto uris = FileUtils::getChildrenUris(m_uri); for (auto uri: uris) { FileNode *node = new FileNode(uri, this, m_reporter); m_children->append(node); node->findChildrenRecursively(); } } } void FileNode::computeTotalSize(goffset *offset) { *offset += m_size; for (auto child : *m_children) { child->computeTotalSize(offset); } } QString FileNode::getRelativePath() { FileNode *n = this; while (n->m_parent) { n = n->m_parent; } GFile *root_file = g_file_new_for_uri(n->m_uri.toUtf8().constData()); GFile *root_file_parent = g_file_get_parent(root_file); GFile *this_file = g_file_new_for_uri(m_uri.toUtf8().constData()); char *relative_path = g_file_get_relative_path(root_file_parent, this_file); QString relativePath = relative_path; g_free(relative_path); g_object_unref(root_file); g_object_unref(root_file_parent); g_object_unref(this_file); return relativePath; } const QString FileNode::resolveDestFileUri(const QString &destRootDir) { QStringList relativePathList; relativePathList.prepend(m_dest_basename); FileNode *parent = this->parent(); while (parent) { relativePathList.prepend(parent->m_dest_basename); parent = parent->parent(); } QString relativePath = relativePathList.join("/"); if (relativePath.endsWith("/")) { relativePath.chop(1); } QString url = FileUtils::urlEncode(destRootDir + "/" + relativePath); setDestUri(url); return url; } peony/libpeony-qt/file-operation/file-link-operation.h0000644000175000017500000000263014205101223022020 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILELINKOPERATION_H #define FILELINKOPERATION_H #include "peony-core_global.h" #include "file-operation.h" namespace Peony { class PEONYCORESHARED_EXPORT FileLinkOperation : public FileOperation { Q_OBJECT public: FileLinkOperation(QString srcUri, QString destDirUri, QObject *parent = nullptr); ~FileLinkOperation() override; std::shared_ptr getOperationInfo() override { return m_info; } void run() override; private: QString m_src_uri = nullptr; QString m_dest_uri = nullptr; std::shared_ptr m_info = nullptr; }; } #endif // FILELINKOPERATION_H peony/libpeony-qt/file-operation/file-operation-error-dialogs.h0000644000175000017500000001413314205115226023645 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Jing Ding * */ #ifndef FILEOPERATIONERRORDIALOGREGULAR_H #define FILEOPERATIONERRORDIALOGREGULAR_H #include #include #include #include #include #include "file-operation-error-dialog-base.h" namespace Peony { class FileRenameDialog; class FileInformationLabel; class FileOperationErrorDialogConflict; /*! * \brief Factory class for error handling pop-up boxes * */ class PEONYCORESHARED_EXPORT FileOperationErrorDialogFactory { public: static FileOperationErrorHandler* getDialog (FileOperationError& errInfo); }; /*! * \brief Error warning pop-up box * ED_WARNING */ class PEONYCORESHARED_EXPORT FileOperationErrorDialogWarning : public FileOperationErrorDialogBase { Q_OBJECT Q_INTERFACES(Peony::FileOperationErrorHandler) public: explicit FileOperationErrorDialogWarning(FileOperationErrorDialogBase *parent = nullptr); ~FileOperationErrorDialogWarning()override; virtual void handle (FileOperationError& error) override; private: float m_margin = 9; float m_pic_top = 63; float m_margin_lr = 26; float m_pic_size = 48; float m_fix_width = 550; float m_fix_height = 188; float m_text_y = 65; float m_text_heigth = 80; float m_ok_x = 410; float m_ok_y = 132; float m_ok_w = 120; float m_ok_h = 36; float m_cancel_x = 280; float m_cancel_y = 132; float m_cancel_w = 120; float m_cancel_h = 36; QLabel* m_icon = nullptr; QLabel* m_text = nullptr; QScrollArea* m_text_scroll = nullptr; QPushButton* m_ok = nullptr; QPushButton* m_cancel = nullptr; }; /*! * \brief Dialog box for handling file conflicts * */ class PEONYCORESHARED_EXPORT FileOperationErrorDialogConflict : public FileOperationErrorDialogBase { Q_OBJECT Q_INTERFACES(Peony::FileOperationErrorHandler) public: explicit FileOperationErrorDialogConflict(FileOperationErrorDialogBase *parent = nullptr); ~FileOperationErrorDialogConflict() override; void setTipFilename (QString name); void setTipFileicon (QString icon); virtual void handle (FileOperationError& error) override; private: float m_margin = 9; float m_margin_lr = 26; float m_fix_width = 550; float m_fix_height = 192; // file icon float m_file_x = 26; float m_file_y = 60; float m_file_size = 48; float m_tip_x = 85; float m_tip_y = 45; float m_tip_width = 430; float m_tip_height = 80; // replace float m_rp_btn_x = 414; float m_rp_btn_y = 140; float m_rp_btn_width = 120; float m_rp_btn_height = 36; // ignore float m_ig_btn_x = 278; float m_ig_btn_y = 140; float m_ig_btn_width = 120; float m_ig_btn_height = 36; // backup float m_bk_btn_x = 142; float m_bk_btn_y = 140; float m_bk_btn_width = 120; float m_bk_btn_height = 36; // Then do the same thing float m_sm_btn_x = 16; float m_sm_btn_y = 150; float m_sm_btn_width = 216; float m_sm_btn_height = 24; float m_ck_btn_top = 385; float m_ck_btn_heigth = 18; float m_btn_top = 80; float m_btn_width = 120; float m_btn_heigth = 38; float m_btn_ok_margin_left = 434; float m_btn_cancel_margin_left = 298; QLabel* m_tip = nullptr; QLabel* m_title = nullptr; QLabel* m_file_icon = nullptr; QString m_file_name; QString m_file_icon_name; QPushButton* m_rp_btn = nullptr; QPushButton* m_ig_btn = nullptr; QPushButton* m_bk_btn = nullptr; QCheckBox* m_sm_ck = nullptr; bool m_ignore = false; bool m_backup = false; bool m_replace = false; bool m_do_same = false; }; /*! * \brief Error warning pop-up box * ED_NOT_SUPPORTED */ class PEONYCORESHARED_EXPORT FileOperationErrorDialogNotSupported : public FileOperationErrorDialogBase { Q_OBJECT Q_INTERFACES(Peony::FileOperationErrorHandler) public: explicit FileOperationErrorDialogNotSupported(FileOperationErrorDialogBase *parent = nullptr); ~FileOperationErrorDialogNotSupported()override; virtual void handle (FileOperationError& error) override; private: float m_margin = 9; float m_pic_top = 63; float m_margin_lr = 26; float m_pic_size = 48; float m_fix_width = 550; float m_fix_height = 188; float m_text_y = 65; float m_text_heigth = 60; float m_ok_x = 410; float m_ok_y = 132; float m_ok_w = 120; float m_ok_h = 36; float m_cancel_x = 280; float m_cancel_y = 132; float m_cancel_w = 120; float m_cancel_h = 36; float m_sm_btn_x = 16; float m_sm_btn_y = 150; float m_sm_btn_width = 200; float m_sm_btn_height = 20; QLabel* m_icon = nullptr; QLabel* m_text = nullptr; QScrollArea* m_text_scroll = nullptr; QPushButton* m_ok = nullptr; QPushButton* m_cancel = nullptr; QCheckBox* m_sm_ck = nullptr; }; }; #endif // FILEOPERATIONERRORDIALOGREGULAR_H peony/libpeony-qt/file-operation/file-node-reporter.h0000644000175000017500000000367514205101223021664 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef FILENODEREPORTER_H #define FILENODEREPORTER_H #include #include #include "peony-core_global.h" namespace Peony { class FileNode; /*! * \brief The FileNodeReporter class *
* This class is a signal proxy of FileNode instances. * Other objects can connect the signals getting the current state of filenode. *
*/ class PEONYCORESHARED_EXPORT FileNodeReporter : public QObject { Q_OBJECT public: explicit FileNodeReporter(QObject *parent = nullptr); ~FileNodeReporter(); void sendNodeFound(const QString &uri, const qint64 &offset) { Q_EMIT nodeFound(uri, offset); } void cancel() { m_cancelled = true; } bool isOperationCancelled() { return m_cancelled; } Q_SIGNALS: void nodeFound(const QString &uri, const qint64 &offset); /*! * \brief enumerateNodeFinished * \deprecated */ void enumerateNodeFinished(); /*! * \brief nodeOperationDone * \param uri * \param offset * \deprecated */ void nodeOperationDone(const QString &uri, const qint64 &offset); private: bool m_cancelled = false; }; } #endif // FILENODEREPORTER_H peony/libpeony-qt/file-operation/README.md0000644000175000017500000000302414205101223017254 0ustar fengfeng# FileOperation ## Brief FileOperation is an QRunnable based interface of several file operation. Such as move, copy, delete, rename, and so on. ## Desgin Concept - Every operation is atomic. That means if you cancelled the operation, it should rollback to the previous states when you didn't execute the operation. - The operation can not handle error itself. When a error ocurred, it will send the signal for an error handler instance, and block itself until the handler's slot return the response type. The simplest handler should ignore all the error, even though the operation might not success. - The error handler is variable. It could have a UI, or just running in backend. - There should be a operation manager to manager the operations' stack for operation undo or redo. ## Implement example -- FileMoveOperation FileMoveOperatoin is dervied from FileOperation. It is an synchronous and cancellable operation. There are two types internal move operation provieded by the class, the native move and the fallback move. The native move is very fast, but it might not be support in many cases, such as move from different computer. The fallback move is actually a copy and delete operation of files. It might spend a lot of times for a large file directory's movement. ## FileOperationProgressWizard FileOperationProgressWizard is used to indicate the progress of a FileOperation instance. Cause the operation running in thread, wizard should connect the providing signal of FileOperation and then it will handle the sending signal internally.peony/libpeony-qt/file-operation/file-link-operation.cpp0000644000175000017500000001066314205101223022360 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-link-operation.h" #include "file-operation-manager.h" #include "gerror-wrapper.h" #include #include using namespace Peony; FileLinkOperation::FileLinkOperation(QString srcUri, QString destDirUri, QObject *parent) : FileOperation (parent) { m_src_uri = srcUri; QUrl url = srcUri; //If it starts with a ".", add it directly to the end if(url.fileName().startsWith('.')){ m_dest_uri = destDirUri + "/" + url.fileName() + " - " + tr("Symbolic Link"); }else{ // Otherwise, add it directly to the front m_dest_uri = destDirUri + "/" + tr("Symbolic Link") + " - " + url.fileName(); } m_dest_uri = QUrl::fromEncoded(m_dest_uri.toUtf8()).toDisplayString(); QStringList fake_uris; fake_uris<(fake_uris, destDirUri, FileOperationInfo::Link); } FileLinkOperation::~FileLinkOperation() { } void FileLinkOperation::run() { operationStarted(); auto destFile = wrapGFile(g_file_new_for_uri(FileUtils::urlEncode(m_dest_uri).toUtf8().constData())); GError *err = nullptr; retry: QUrl url = m_src_uri; g_file_make_symbolic_link(destFile.get()->get(), url.path().toUtf8().constData(), nullptr, &err); if (err) { setHasError(true); //forbid response actions except retry and cancel. FileOperationError except; except.srcUri = m_src_uri; except.errorType = ET_GIO; except.isCritical = true; except.errorStr = err->message; except.errorCode = err->code; except.op = FileOpLink; except.title = tr("Link file error"); except.destDirUri = m_dest_uri; auto responseType = Invalid; if (G_IO_ERROR_EXISTS == err->code) { except.dlgType = ED_WARNING; Q_EMIT errored(except); responseType = except.respCode; } else { except.dlgType = ED_WARNING; Q_EMIT errored(except); responseType = except.respCode; } if (responseType == Peony::Retry) { goto retry; } else if (responseType == Peony::Cancel) { goto end; } } g_file_set_display_name(destFile.get()->get(), QUrl::fromPercentEncoding(m_dest_uri.split("/").last().toUtf8()).toUtf8().constData(), nullptr, nullptr); end: // maybe not need sync ??? fileSync(m_src_uri, m_dest_uri); // judge if the operation should sync. // bool needSync = false; // GFile *src_first_file = g_file_new_for_uri(m_src_uri.toUtf8().constData()); // GMount *src_first_mount = g_file_find_enclosing_mount(src_first_file, nullptr, nullptr); // if (src_first_mount) { // needSync = g_mount_can_unmount(src_first_mount); // g_object_unref(src_first_mount); // } else { // // maybe a vfs file. // needSync = true; // } // g_object_unref(src_first_file); // GFile *dest_dir_file = g_file_new_for_uri(m_dest_uri.toUtf8().constData()); // GMount *dest_dir_mount = g_file_find_enclosing_mount(dest_dir_file, nullptr, nullptr); // if (src_first_mount) { // needSync = g_mount_can_unmount(dest_dir_mount); // g_object_unref(dest_dir_mount); // } else { // needSync = true; // } // g_object_unref(dest_dir_file); // //needSync = true; // if (needSync) { // auto path = g_file_get_path(destFile.get()->get()); // if (path) { // operationStartSnyc(); // QProcess p; // p.start(QString("sync -f '%1'").arg(path)); // p.waitForFinished(-1); // g_free(path); // } // } operationFinished(); //notifyFileWatcherOperationFinished(); } peony/libpeony-qt/thumbnail-manager.cpp0000644000175000017500000003110214205115226017165 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019-2020, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * Burgess Chang * */ #include "thumbnail-manager.h" #include "file-info-manager.h" #include "file-watcher.h" #include "file-utils.h" #include "thumbnail/pdf-thumbnail.h" #include "thumbnail/video-thumbnail.h" #include "thumbnail/office-thumbnail.h" #include "thumbnail/image-pdf-thumbnail.h" #include "generic-thumbnailer.h" #include "thumbnail-job.h" #include "global-settings.h" #include #include #include #include #include #include using namespace Peony; static ThumbnailManager *global_instance = nullptr; static bool m_tril_exist = false; /*! * \brief ThumbnailManager::ThumbnailManager * \param parent * \bug * thumbnail will do i/o on the file. if we write on a pictrue and save * save it, the image editor might report a modified error due to we hold * the file in peony-qt. * * this bug is not critical, but i have to consider current thumbnailer * might be a bad desgin. */ ThumbnailManager::ThumbnailManager(QObject *parent) : QObject(parent) { GlobalSettings::getInstance(); m_thumbnail_thread_pool = new QThreadPool(this); m_thumbnail_thread_pool->setMaxThreadCount(1); m_semaphore = new QSemaphore(1); findAtril(); } ThumbnailManager::~ThumbnailManager() { delete m_semaphore; } ThumbnailManager *ThumbnailManager::getInstance() { if (!global_instance) global_instance = new ThumbnailManager; return global_instance; } void ThumbnailManager::syncThumbnailPreferences() { GlobalSettings::getInstance()->forceSync(FORBID_THUMBNAIL_IN_VIEW); } void ThumbnailManager::insertOrUpdateThumbnail(const QString &uri, const QIcon &icon) { m_semaphore->acquire(); m_hash.remove(uri); m_hash.insert(uri, icon); m_semaphore->release(); } void ThumbnailManager::setForbidThumbnailInView(bool forbid) { GlobalSettings::getInstance()->setValue(FORBID_THUMBNAIL_IN_VIEW, forbid); } void ThumbnailManager::createVideFileThumbnail(const QString &uri, std::shared_ptr watcher) { QIcon thumbnail; VideoThumbnail videoThumbnail(uri); thumbnail = videoThumbnail.generateThumbnail(); if (!thumbnail.isNull()) { insertOrUpdateThumbnail(uri, thumbnail); if (watcher) { watcher->fileChanged(uri); } } return; } void ThumbnailManager::createImagePdfFileThumbnail(const QString &uri, std::shared_ptr watcher) { QIcon thumbnail; ImagePdfThumbnail officeThumbnail(uri); thumbnail = officeThumbnail.generateThumbnail();; if (!thumbnail.isNull()) { insertOrUpdateThumbnail(uri, thumbnail); if (watcher) { watcher->fileChanged(uri); } } return; } void ThumbnailManager::createPdfFileThumbnail(const QString &uri, std::shared_ptr watcher) { QIcon thumbnail; QUrl url = uri; if (!uri.startsWith("file:///")) { url = FileUtils::getTargetUri(uri); //qDebug()<fileChanged(uri); } } return; } void ThumbnailManager::createImageFileThumbnail(const QString &uri, std::shared_ptr watcher) { QUrl url = uri; if (!uri.startsWith("file:///")) { url = FileUtils::getTargetUri(uri); //qDebug()<fileChanged(uri); } } //qApp->processEvents(); return; } void ThumbnailManager::createOfficeFileThumbnail(const QString &uri, std::shared_ptr watcher) { QIcon thumbnail; OfficeThumbnail officeThumbnail(uri); thumbnail = officeThumbnail.generateThumbnail();; if (!thumbnail.isNull()) { insertOrUpdateThumbnail(uri, thumbnail); if (watcher) { watcher->fileChanged(uri); } } return; } void ThumbnailManager::createDesktopFileThumbnail(const QString &uri, std::shared_ptr watcher) { QIcon thumbnail; QUrl url = uri; if (!uri.startsWith("file:///")) { url = FileUtils::getTargetUri(uri); } auto _desktop_file = g_desktop_app_info_new_from_filename(url.path().toUtf8().constData()); if (!_desktop_file) { return; } auto _icon_string = g_desktop_app_info_get_string(_desktop_file, "Icon"); thumbnail = QIcon::fromTheme(_icon_string); QString string = _icon_string; if (thumbnail.isNull()) { if (string.startsWith("/")) { thumbnail = GenericThumbnailer::generateThumbnail(_icon_string, true); } else if (string.contains(".")) { // try getting themed icon with image suffix. string.chop(string.count() - string.lastIndexOf(".")); thumbnail = QIcon::fromTheme(string); } } //add special path search /use/share/pixmaps if (thumbnail.isNull()) { QString path = QString("/usr/share/pixmaps/%1.%2").arg(_icon_string).arg("png"); QString path_svg = QString("/usr/share/pixmaps/%1.%2").arg(_icon_string).arg("svg"); //qDebug() << "createDesktopFileThumbnail path:" <fileChanged(uri); } } return; } //is system has atril software void ThumbnailManager::findAtril() { QtConcurrent::run([](){ GList *infos = g_app_info_get_all(); GList *l = infos; while (l && ! m_tril_exist) { const char *cmd = g_app_info_get_executable(static_cast(l->data)); QString tmp = cmd; if (tmp.contains("atril")) { m_tril_exist = true; } l = l->next; } g_list_free_full(infos, g_object_unref); }); } void ThumbnailManager::createThumbnailInternal(const QString &uri, std::shared_ptr watcher, bool force) { 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:" <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->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 watcher, bool force) { qDebug() <<"createThumbnail:" <thumbnailUpdated(uri); watcher->fileChanged(uri); qDebug() <<"createThumbnail return:" <customIcon().isEmpty() && info->customIcon().startsWith("/")) needThumbnail = true; if (!info->mimeType().isEmpty()) { if (info->isImageFile()) { needThumbnail = true; } else if (info->mimeType().contains("pdf")) { needThumbnail = true; } else if(info->isVideoFile()) { needThumbnail = true; } else if (info->isOfficeFile()) { needThumbnail = true; } else if (info->uri().endsWith(".desktop")) { if (thumbnail.isNull()) { needThumbnail = false; updateDesktopFileThumbnail(uri, watcher); } else needThumbnail = true; } } if (!needThumbnail) return; auto thumbnailJob = new ThumbnailJob(uri, watcher, this); m_thumbnail_thread_pool->start(thumbnailJob); qDebug() <<"createThumbnail thumbnailJob start:" < watcher) { auto info = FileInfo::fromUri(uri); if (info->uri().endsWith(".desktop")) { //qDebug()<<"is desktop file"<thumbnailUpdated(uri); } } } void ThumbnailManager::clearThumbnail() { m_semaphore->acquire(); if (!m_hash.isEmpty()) { m_hash.clear(); } m_semaphore->release(); } void ThumbnailManager::releaseThumbnail(const QString &uri) { m_semaphore->acquire(); m_hash.remove(uri); m_semaphore->release(); } void ThumbnailManager::releaseThumbnail(const QStringList &uris) { m_semaphore->acquire(); for (auto uri : uris) { m_hash.remove(uri); } m_semaphore->release(); } const QIcon ThumbnailManager::tryGetThumbnail(const QString &uri) { m_semaphore->acquire(); auto icon = m_hash.value(uri); m_semaphore->release(); return icon; } peony/libpeony-qt/usershare-manager.h0000644000175000017500000000421614205115226016656 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2021, 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 . * * Authors: yanling * */ #ifndef USERSHARE_MANAGER_H #define USERSHARE_MANAGER_H #include #include #include //#include #include "peony-core_global.h" namespace Peony { class PEONYCORESHARED_EXPORT ShareInfo { public: ShareInfo& operator= (const ShareInfo*); QString name; QString comment; QString originalPath; bool readOnly = true; bool allowGuest = false; bool isShared = false; }; class PEONYCORESHARED_EXPORT UserShareInfoManager : public QObject { Q_OBJECT public: static UserShareInfoManager* getInstance (); QString exectueCommand (QStringList& args, bool* ret /* out */, QString sharedPath=""); bool hasSharedInfo (QString& name); void removeShareInfo (QString& name); bool addShareInfo (ShareInfo* shareInfo); bool updateShareInfo (ShareInfo& shareInfo); const ShareInfo* getShareInfo (QString& name); private: explicit UserShareInfoManager (QObject* parent = nullptr) : QObject(parent) {}; Q_SIGNALS: void signal_addSharedFolder(const ShareInfo& shareInfo, bool successed); void signal_deleteSharedFolder(const QString& originalPath, bool successed); private: bool m_bInit = false; QMutex m_mutex; QMap m_sharedInfoMap; static UserShareInfoManager* g_shareInfo; }; } #endif // USERSHARE_MANAGER_H peony/libpeony-qt/volume-manager.cpp0000644000175000017500000003031214205115226016513 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "volume-manager.h" #include "file-utils.h" #include "gobject-template.h" #include #include #include #include "file-info.h" #include using namespace Peony; static VolumeManager *m_global_manager = nullptr; VolumeManager::VolumeManager(QObject *parent) : QObject(parent) { m_volume_monitor = g_volume_monitor_get(); m_drive_connected_handle = g_signal_connect(m_volume_monitor, "drive-connected", G_CALLBACK(drive_connected_callback), this); m_drive_disconnected_handle = g_signal_connect(m_volume_monitor, "drive-disconnected", G_CALLBACK(drive_disconnected_callback), this); m_volume_added_handle = g_signal_connect(m_volume_monitor, "volume-added", G_CALLBACK(volume_added_callback), this); m_volume_removed_handle = g_signal_connect(m_volume_monitor, "volume-removed", G_CALLBACK(volume_removed_callback), this); m_mount_added_handle = g_signal_connect(m_volume_monitor, "mount-added", G_CALLBACK(mount_added_callback), this); m_mount_removed_handle = g_signal_connect(m_volume_monitor, "mount-removed", G_CALLBACK(mount_removed_callback), this); } VolumeManager::~VolumeManager() { disconnect(); g_signal_handler_disconnect(m_volume_monitor, m_drive_connected_handle); g_signal_handler_disconnect(m_volume_monitor, m_drive_disconnected_handle); g_signal_handler_disconnect(m_volume_monitor, m_volume_added_handle); g_signal_handler_disconnect(m_volume_monitor, m_volume_removed_handle); g_signal_handler_disconnect(m_volume_monitor, m_mount_added_handle); g_signal_handler_disconnect(m_volume_monitor, m_mount_removed_handle); g_object_unref(m_volume_monitor); } VolumeManager *VolumeManager::getInstance() { if (!m_global_manager) { m_global_manager = new VolumeManager; } return m_global_manager; } void VolumeManager::drive_connected_callback(GVolumeMonitor *monitor, GDrive *drive, VolumeManager *p_this) { Q_UNUSED(monitor); auto data = std::make_shared(drive); Q_EMIT p_this->driveConnected(data); } void VolumeManager::drive_disconnected_callback(GVolumeMonitor *monitor, GDrive *drive, VolumeManager *p_this) { Q_UNUSED(monitor); Q_EMIT p_this->driveDisconnected(std::make_shared(drive)); } void VolumeManager::volume_added_callback(GVolumeMonitor *monitor, GVolume *volume, VolumeManager *p_this) { Q_UNUSED(monitor); Q_EMIT p_this->volumeAdded(std::make_shared(volume)); } void VolumeManager::volume_removed_callback(GVolumeMonitor *monitor, GVolume *volume, VolumeManager *p_this) { Q_UNUSED(monitor); Q_EMIT p_this->volumeRemoved(std::make_shared(volume)); } void VolumeManager::mount_added_callback(GVolumeMonitor *monitor, GMount *mount, VolumeManager *p_this) { Q_UNUSED(monitor); Q_EMIT p_this->mountAdded(std::make_shared(mount)); } void VolumeManager::mount_removed_callback(GVolumeMonitor *monitor, GMount *mount, VolumeManager *p_this) { Q_UNUSED(monitor); Q_EMIT p_this->mountRemoved(std::make_shared(mount)); } void VolumeManager::unmount_cb(GFile *file, GAsyncResult *result, GError **error, QString *targetUri) { bool successed = g_file_unmount_mountable_with_operation_finish(file, result, error); if (!successed) { if (error) { auto err = GErrorWrapper::wrapFrom(g_error_copy(*error)); QMessageBox::warning(nullptr, tr("Error"), err.get()->message()); } } else { VolumeManager::getInstance()->fileUnmounted(*targetUri); } delete targetUri; } void VolumeManager::unmount(const QString &uri) { auto targetUri = new QString; *targetUri = FileInfo::fromUri(uri).get()->targetUri(); auto file = wrapGFile(g_file_new_for_uri(uri.toUtf8().constData())); g_file_unmount_mountable_with_operation(file.get()->get(), G_MOUNT_UNMOUNT_NONE, nullptr, nullptr, GAsyncReadyCallback(unmount_cb), targetUri); } //FIXME: should i add async support? std::shared_ptr VolumeManager::getMountFromUri(const QString &uri) { GFile *file = g_file_new_for_uri(uri.toUtf8().constData()); if (!file) return nullptr; std::shared_ptr tmp = nullptr; GError *err = nullptr; GMount *mount = g_file_find_enclosing_mount(file, nullptr, &err); if (err) { qDebug()<message; g_error_free(err); } g_object_unref(file); if (mount) { tmp = std::make_shared(mount, true); } return tmp; } std::shared_ptr VolumeManager::getDriveFromUri(const QString &uri) { GFile *file = g_file_new_for_uri(uri.toUtf8().constData()); if (!file) return nullptr; std::shared_ptr tmp; GMount *mount = g_file_find_enclosing_mount(file, nullptr, nullptr); g_object_unref(file); if (mount) { GDrive *drive = g_mount_get_drive(mount); if (drive) { tmp = std::make_shared(drive, true); } g_object_unref(mount); } return tmp; } std::shared_ptr VolumeManager::getVolumeFromUri(const QString &uri) { GFile *file = g_file_new_for_uri(uri.toUtf8().constData()); if (!file) return nullptr; std::shared_ptr tmp; GMount *mount = g_file_find_enclosing_mount(file, nullptr, nullptr); g_object_unref(file); if (mount) { GVolume *volume = g_mount_get_volume(mount); if (volume) { tmp = std::make_shared(volume, true); } g_object_unref(mount); } return tmp; } std::shared_ptr VolumeManager::getDriveFromMount(const std::shared_ptr &mount) { GMount *m = mount->getGMount(); if (!m) { return nullptr; } GDrive *drive = g_mount_get_drive(m); std::shared_ptr tmp; if (!drive) { return nullptr; } tmp = std::make_shared(drive, true); return tmp; } std::shared_ptr VolumeManager::getVolumeFromMount(const std::shared_ptr &mount) { GMount *m = mount->getGMount(); if (!m) { return nullptr; } GVolume *volume = g_mount_get_volume(m); std::shared_ptr tmp; if (!volume) { return nullptr; } tmp = std::make_shared(volume, true); return tmp; } /* Lookup GDrive* from system volume monitor for @unixPath. * @unixPath: eg: "/dev/sdb1" */ std::shared_ptr VolumeManager::getDriveFromSystemByPath(const QString &unixPath) { std::shared_ptr tmp = nullptr; QFileInfo deviceFile; GList *allVolumes,*l; GDrive *gdrive = NULL; GVolume *gvolume = NULL; char *tmpPath = NULL; deviceFile.setFile(unixPath); if(!deviceFile.exists()) return nullptr; allVolumes = g_volume_monitor_get_volumes(m_volume_monitor); for(l = allVolumes; l != NULL; l = l->next){ gvolume = (GVolume*)l->data; tmpPath = g_volume_get_identifier(gvolume,G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); if(tmpPath){ if(!unixPath.compare(tmpPath)){ gdrive = g_volume_get_drive(gvolume); g_free(tmpPath); break; } g_free(tmpPath); } } g_list_foreach(allVolumes,(GFunc)g_object_unref,NULL); g_list_free(allVolumes); if(gdrive) tmp = std::make_shared(gdrive,true); return tmp; } /* Find the corresponding /dev device based on the mount point. * @mountPoint: it should not start with "file:///". for example /media/user/aaa is correct. * @return: return /dev device path or NULL */ const char* VolumeManager::getUnixDeviceFileFromMountPoint(const char* mountPoint) { if(!mountPoint) return NULL; const char *deviceFilePath = NULL; GUnixMountEntry* mountEntry = g_unix_mount_for(mountPoint,NULL); if(mountEntry) deviceFilePath = g_unix_mount_get_device_path(mountEntry); return deviceFilePath; } QString Drive::name() { if (!m_drive) return nullptr; char *name = g_drive_get_name(m_drive); QString value = name; g_free(name); return value; } QString Drive::iconName() { if (!m_drive) return nullptr; GIcon *g_icon = g_drive_get_icon(m_drive); const gchar* const* icon_names = g_themed_icon_get_names(G_THEMED_ICON (g_icon)); QString iconName; if(icon_names) { iconName= *icon_names; } else { g_autofree gchar *icon_name = g_icon_to_string(g_icon); iconName = icon_name; } g_object_unref(g_icon); if (iconName.isEmpty()) return "drive-harddisk"; return iconName; } QString Drive::symbolicIconName() { if (!m_drive) return nullptr; GThemedIcon *g_icon = G_THEMED_ICON(g_drive_get_symbolic_icon(m_drive)); const gchar* const* icon_names = g_themed_icon_get_names(G_THEMED_ICON (g_icon)); g_object_unref(g_icon); if (! icon_names) return "drive-harddisk"; return *icon_names; } QString Volume::name() { char *name = g_volume_get_name(m_volume); QString value = name; g_free(name); return value; } QString Volume::iconName() { GIcon *g_icon = g_volume_get_icon(m_volume); const gchar* const* icon_names = g_themed_icon_get_names(G_THEMED_ICON (g_icon)); QString iconName; if(icon_names) { iconName= *icon_names; } else { g_autofree gchar *icon_name = g_icon_to_string(g_icon); iconName = icon_name; } g_object_unref(g_icon); if (iconName.isEmpty()) return "drive-harddisk"; return iconName; } QString Volume::symbolicIconName() { GThemedIcon *g_icon = G_THEMED_ICON(g_volume_get_symbolic_icon(m_volume)); const gchar* const* icon_names = g_themed_icon_get_names(G_THEMED_ICON (g_icon)); g_object_unref(g_icon); if (! icon_names) return "drive-harddisk"; return *icon_names; } QString Mount::name() { char *name = g_mount_get_name(m_mount); QString value = name; g_free(name); return value; } QString Mount::iconName() { GIcon *g_icon = g_mount_get_icon(m_mount); const gchar* const* icon_names = g_themed_icon_get_names(G_THEMED_ICON (g_icon)); QString iconName; if(icon_names) { iconName= *icon_names; } else { g_autofree gchar *icon_name = g_icon_to_string(g_icon); iconName = icon_name; } g_object_unref(g_icon); if (iconName.isEmpty()) return "drive-harddisk"; return iconName; } QString Mount::symbolicIconName() { GThemedIcon *g_icon = G_THEMED_ICON(g_mount_get_symbolic_icon(m_mount)); const gchar* const* icon_names = g_themed_icon_get_names(G_THEMED_ICON (g_icon)); g_object_unref(g_icon); if (! icon_names) return "drive-harddisk"; return *icon_names; } peony/libpeony-qt/file-info.h0000644000175000017500000002356714205115226015127 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * Authors: Meihong He * */ #ifndef FILEINFO_H #define FILEINFO_H #include "peony-core_global.h" #include #include #include #include #include #include #include namespace Peony { class FileInfoJob; class FileMetaInfo; static char *office_mime_types[] = { "application/wps-office.doc", "application/msword", "application/vnd.ms-word", "application/x-msword", "application/vnd.ms-word.document.macroenabled.12", "application/wps-office.dot", "application/msword-template", "application/vnd.ms-word.template.macroenabled.12", "application/wps-office.dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template", "application/wps-office.docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/wps-office.wps", "application/vnd.ms-works", "application/wps-office.wpt", "application/wps-office.ppt", "application/vnd.ms-powerpoint", "application/powerpoint", "application/mspowerpoint", "application/x-mspowerpoint", "application/vnd.ms-powerpoint.presentation.macroenabled.12", "application/wps-office.pot", "application/vnd.ms-powerpoint.template.macroenabled.12", "application/wps-office.potx", "application/vnd.openxmlformats-officedocument.presentationml.template", "application/wps-office.pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation", "application/vnd.openxmlformats-officedocument.presentationml.slideshow", "application/wps-office.dps", "application/wps-office.dpt", "application/wps-office.xls", "application/vnd.ms-excel", "application/msexcel", "application/x-msexcel", "application/vnd.ms-excel.sheet.macroenabled.12", "application/vnd.ms-excel.template.macroenabled.12", "application/wps-office.xlt", "application/wps-office.xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "application/wps-office.et", "application/wps-office.ett", "end" }; /*! * \brief The FileInfo class *
* This class represents the information of this file and file itself. * Although you can use constructor new a FileInfo instance, * you should use FileInfo::fromUri(). Because it provides an shared data, * and FileInfoJob need hold a shared_ptr reference, too. * This will help to reduce the risk of memory leaks. *
*/ class PEONYCORESHARED_EXPORT FileInfo : public QObject { friend class FileInfoJob; friend class FileMetaInfo; Q_OBJECT public: enum Access { Readable, Writeable, Executable, Deleteable, Trashable, Renameable }; Q_DECLARE_FLAGS(AccessFlags, Access) explicit FileInfo(QObject *parent = nullptr); explicit FileInfo(const QString &uri, QObject *parent = nullptr); ~FileInfo(); /*! * \brief fromUri * \param uri * \param addToHash * \return * \deprecated */ static std::shared_ptr fromUri(QString uri); /*! * \brief fromPath * \param path * \param addToHash * \return * \deprecated */ static std::shared_ptr fromPath(QString path); /*! * \brief fromGFile * \param file * \param addToHash * \return * \deprecated */ static std::shared_ptr fromGFile(GFile *file); QString uri() { return m_uri; } bool isDir() { return m_is_dir || m_content_type == "inode/directory"; } bool isVolume() { return m_is_volume; } bool isSymbolLink() { return m_is_symbol_link; } bool isVirtual() { return m_is_virtual; } bool isValid() { return m_is_valid; } QString desktopName(){ return m_desktop_name; } QString iconName() { return m_icon_name; } QString symbolicIconName() { return m_symbolic_icon_name; } QString fileID() { return m_file_id; } QString mimeType() { return m_mime_type_string; } QString fileType() { return m_file_type; } QString filePath() { return m_path; } QString fileSize() { return m_file_size; } QString modifiedDate() { return m_modified_date; } QString accessDate() { return m_access_date; } QString deletionDate() { return m_deletion_date; } QString type() { return m_content_type; } QString fileSystemType() { return m_fs_type; } quint64 size() { return m_size; } quint64 modifiedTime() { return m_modified_time; } quint64 accessTime() { return m_access_time; } quint64 deletionTime() { return m_deletion_date_uint64; } QList getColors() { return m_colors; } bool canRead() { return m_can_read; } bool canWrite() { return m_can_write; } bool canExecute() { return m_can_excute; } bool canDelete() { return m_can_delete; } bool canTrash() { return m_can_trash; } bool canRename() { return m_can_rename; } bool canMount() { return m_can_mount; } bool canUnmount() { return m_can_unmount; } bool canEject() { return m_can_eject; } bool canStart() { return m_can_start; } bool canStop() { return m_can_stop; } bool isDesktopFile() { return m_can_excute && m_uri.endsWith(".desktop"); } bool isPdfFile(){ return m_mime_type_string.contains("pdf"); } bool isImageFile(){ return m_mime_type_string.startsWith("image/"); } bool isImagePdfFile(){ return m_mime_type_string.contains("djvu"); } bool isVideoFile(); bool isOfficeFile(); bool isEmptyInfo() { return m_display_name == nullptr || m_display_name == ""; } AccessFlags accesses() { auto flags = AccessFlags(); #if (QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)) flags.setFlag(Readable, m_can_read); flags.setFlag(Writeable, m_can_write); flags.setFlag(Executable, m_can_excute); flags.setFlag(Deleteable, m_can_delete); flags.setFlag(Trashable, m_can_trash); flags.setFlag(Renameable, m_can_rename); return flags; #else flags = 0; if (m_can_read) flags |= Readable; if (m_can_write) flags |= Writeable; if (m_can_excute) flags |= Executable; if (m_can_delete) flags |= Deleteable; if (m_can_trash) flags |= Trashable; if (m_can_rename) flags |= Renameable; #endif } GFile *gFileHandle() { return m_file; } const QString targetUri(); const QString displayName(); const QString symlinkTarget(); const QString unixDeviceFile(); const QString customIcon(); quint64 getDeletionDateUInt64(); //const QIcon thumbnail() {return m_thumbnail;} //void setThumbnail(const QIcon &thumbnail) {m_thumbnail = thumbnail;} Q_SIGNALS: void updated(); private: QString m_uri = nullptr; bool m_is_valid = false; bool m_is_dir = false; bool m_is_volume = false; bool m_is_remote = false; bool m_is_symbol_link = false; bool m_is_virtual = false; bool m_is_loaded = false; QString m_display_name = nullptr; QString m_desktop_name = nullptr; QString m_icon_name = nullptr; QString m_symbolic_icon_name = nullptr; QString m_file_id = nullptr; QString m_path = nullptr; QString m_content_type = nullptr; guint64 m_size = 0; guint64 m_modified_time = 0; guint64 m_access_time = 0; guint64 m_deletion_date_uint64 = 0; /*! * \deprecated * \brief m_mime_type_string * \see m_content_type */ QString m_mime_type_string = nullptr; QString m_file_type = nullptr; QString m_file_size = nullptr; QString m_modified_date = nullptr; QString m_access_date = nullptr; QString m_deletion_date = nullptr; //access bool m_can_read = true; bool m_can_write = false; bool m_can_excute = false; bool m_can_delete = false; bool m_can_trash = false; bool m_can_rename = false; bool m_can_mount = false; bool m_can_unmount = false; bool m_can_eject = false; bool m_can_start = false; bool m_can_stop = false; QString m_unix_device_file; //FIXME: should i use smart pointer wrap these data? GFile *m_file = nullptr; GFile *m_parent = nullptr; QString m_target_uri; QString m_symlink_target; // filesystem QString m_fs_type; /*! * \brief m_cancellable * This cancellable is used in async query file info in FileInfoJob instance. */ GCancellable *m_cancellable = nullptr; //QIcon m_thumbnail; std::shared_ptr m_meta_info = nullptr; QList m_colors; QMutex m_mutex; }; } #endif // FILEINFO_H peony/libpeony-qt/linux-pwd-helper.h0000644000175000017500000000336014205115226016450 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef LINUXPWDHELPER_H #define LINUXPWDHELPER_H #include #include #include /*! * \brief The PWDItem class * is the abstraction of passwd struct in linux. */ class PWDItem { public: explicit PWDItem(passwd *user); ~PWDItem() {} const QString userName() { return m_user_name; } int userId() { return m_uid; } int groupId() { return m_gid; } const QString fullName() const { return m_full_name; } const QString homeDir() const { return m_home_dir; } const QString shellDir() const { return m_shell_dir; } private: QString m_user_name; QString m_full_name; QString m_home_dir; QString m_shell_dir; int m_uid = -1; int m_gid = -1; }; class LinuxPWDHelper { public: static const QList getAllUserInfos(); static const PWDItem getCurrentUser(); private: LinuxPWDHelper(); }; #endif // LINUXPWDHELPER_H peony/libpeony-qt/thumbnail-manager.h0000644000175000017500000000551314205101223016631 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef THUMBNAILMANAGER_H #define THUMBNAILMANAGER_H #include #include "peony-core_global.h" #include "file-info.h" #include #include #include class QThreadPool; class QSemaphore; namespace Peony { class FileWatcher; class PEONYCORESHARED_EXPORT ThumbnailManager : public QObject { friend class ThumbnailJob; Q_OBJECT public: static ThumbnailManager *getInstance(); void setForbidThumbnailInView(bool forbid); bool hasThumbnail(const QString &uri) { return !m_hash.values(uri).isEmpty(); } void createThumbnail(const QString &uri, std::shared_ptr watcher = nullptr, bool force = false); void clearThumbnail(); void releaseThumbnail(const QString &uri); void releaseThumbnail(const QStringList &uris); void updateDesktopFileThumbnail(const QString &uri, std::shared_ptr watcher = nullptr); const QIcon tryGetThumbnail(const QString &uri); Q_SIGNALS: public Q_SLOTS: void syncThumbnailPreferences(); protected: void insertOrUpdateThumbnail(const QString &uri, const QIcon &icon); private: explicit ThumbnailManager(QObject *parent = nullptr); ~ThumbnailManager(); void createThumbnailInternal(const QString &uri, std::shared_ptr watcher = nullptr, bool force = false); void createVideFileThumbnail(const QString &uri, std::shared_ptr watcher); void createPdfFileThumbnail(const QString &uri, std::shared_ptr watcher); void createImageFileThumbnail(const QString &uri, std::shared_ptr watcher); void createOfficeFileThumbnail(const QString &uri, std::shared_ptr watcher); void createDesktopFileThumbnail(const QString &uri, std::shared_ptr watcher); //djvu file process void findAtril(); void createImagePdfFileThumbnail(const QString &uri, std::shared_ptr watcher); QHash m_hash; //QMutex m_mutex; QThreadPool *m_thumbnail_thread_pool; QSemaphore *m_semaphore; }; } #endif // THUMBNAILMANAGER_H peony/libpeony-qt/file-info-job.cpp0000644000175000017500000003430714205115226016224 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "file-info-job.h" #include "file-info.h" #include "file-meta-info.h" #include "file-info-manager.h" #include "file-label-model.h" #include #include #include #include #include #include #include #include using namespace Peony; FileInfoJob::FileInfoJob(std::shared_ptr info, QObject *parent) : QObject(parent) { m_info = info; //connect(m_info.get(), &FileInfo::updated, this, &FileInfoJob::infoUpdated); m_cancellable = g_cancellable_new(); m_fs_cancellable = g_cancellable_new(); } FileInfoJob::FileInfoJob(const QString &uri, QObject *parent) : QObject (parent) { auto info = FileInfo::fromUri(uri); m_info = info; //connect(m_info.get(), &FileInfo::updated, this, &FileInfoJob::infoUpdated); m_cancellable = g_cancellable_new(); m_fs_cancellable = g_cancellable_new(); } FileInfoJob::~FileInfoJob() { g_object_unref(m_cancellable); g_object_unref(m_fs_cancellable); } void FileInfoJob::cancel() { //NOTE: do not use same cancellble for cancelling, otherwise all job might be cancelled. g_cancellable_cancel(m_cancellable); g_object_unref(m_cancellable); m_cancellable = g_cancellable_new(); g_cancellable_cancel(m_fs_cancellable); g_object_unref(m_fs_cancellable); m_fs_cancellable = g_cancellable_new(); } bool FileInfoJob::querySync() { FileInfo *info = nullptr; if (auto data = m_info.get()) { info = data; } else { if (m_auto_delete) deleteLater(); return false; } GError *err = nullptr; auto _info = g_file_query_info(info->m_file, "standard::*," "time::*," "access::*," "mountable::*," "metadata::*," "trash::*," G_FILE_ATTRIBUTE_ID_FILE, G_FILE_QUERY_INFO_NONE, nullptr, &err); if (err) { qDebug()<code<message; g_error_free(err); if (m_auto_delete) deleteLater(); return false; } auto _fs_info = g_file_query_filesystem_info(info->m_file, "filesystem::*,", m_fs_cancellable, &err); if (err) { qDebug()<code<message; g_error_free(err); if (m_auto_delete) deleteLater(); return false; } refreshInfoContents(_info); refreshFileSystemInfo(_fs_info); g_object_unref(_info); if (m_auto_delete) deleteLater(); infoUpdated(); return true; } GAsyncReadyCallback FileInfoJob::query_info_async_callback(GFile *file, GAsyncResult *res, FileInfoJob *thisJob) { //qDebug()<<"query_info_async_callback"<m_info->uri(); GError *err = nullptr; GFileInfo *_info = g_file_query_info_finish(file, res, &err); if (_info != nullptr) { thisJob->refreshInfoContents(_info); g_object_unref(_info); Q_EMIT thisJob->queryAsyncFinished(true); Q_EMIT thisJob->infoUpdated(); } else { if (err) { qDebug()<code<message; g_error_free(err); } Q_EMIT thisJob->queryAsyncFinished(false); return nullptr; } return nullptr; } void FileInfoJob::refreshFileSystemInfo(GFileInfo *new_info) { FileInfo *info = nullptr; if (auto data = m_info) { info = data.get(); } else { return; } // fs type m_info->m_fs_type = g_file_info_get_attribute_string (new_info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE); Q_EMIT info->updated(); } void FileInfoJob::queryAsync() { FileInfo *info = nullptr; if (auto data = m_info) { info = data.get(); cancel(); } else { Q_EMIT queryAsyncFinished(false); return; } g_file_query_info_async(info->m_file, "standard::*," "time::*," "access::*," "mountable::*," "metadata::*," "trash::*," G_FILE_ATTRIBUTE_ID_FILE, G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, m_cancellable, GAsyncReadyCallback(query_info_async_callback), this); if (m_auto_delete) connect(this, &FileInfoJob::queryAsyncFinished, this, &FileInfoJob::deleteLater, Qt::QueuedConnection); } void FileInfoJob::queryFileType(GFileInfo* new_info){ FileInfo *info = nullptr; if (auto data = m_info) { info = data.get(); } else { return; } GFileType type = g_file_info_get_file_type (new_info); switch (type) { case G_FILE_TYPE_DIRECTORY: //qDebug()<<"dir"; info->m_is_dir = true; break; case G_FILE_TYPE_MOUNTABLE: //qDebug()<<"mountable"; info->m_is_volume = true; break; default: break; } } void FileInfoJob::queryFileDisplayName(GFileInfo* new_info){ FileInfo *info = nullptr; if (auto data = m_info) { info = data.get(); } else { return; } info->m_display_name = QString (g_file_info_get_display_name(new_info)); if (info->isDesktopFile()) { info->m_desktop_name = info->displayName(); QUrl url = info->uri(); GDesktopAppInfo *desktop_info = g_desktop_app_info_new_from_filename(url.path().toUtf8()); if (!desktop_info) { m_info->m_mutex.unlock(); info->updated(); return; } #if GLIB_CHECK_VERSION(2, 56, 0) auto string = g_desktop_app_info_get_locale_string(desktop_info, "Name"); #else //FIXME: should handle locale? //change "Name" to QLocale::system().name(), //try to fix Qt5.6 untranslated desktop file issue auto key = "Name[" + QLocale::system().name() + "]"; auto string = g_desktop_app_info_get_string(desktop_info, key.toUtf8().constData()); #endif qDebug() << "get name string:"<uri()<displayName(); QString path = "/usr/share/applications/" + info->displayName(); if(QFileInfo::exists(url.path().toUtf8()) && QFileInfo::exists(path)) { 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{ info->m_display_name = string; } if (string) g_free(string); g_object_unref(desktop_info); } } void FileInfoJob::refreshInfoContents(GFileInfo *new_info) { // if (!m_info->m_mutex.tryLock(300)) // return; FileInfo *info = nullptr; if (auto data = m_info) { info = data.get(); } else { return; } queryFileType(new_info); 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); } else { // we assume an unknow access file is readable. info->m_can_read = true; } if (g_file_info_has_attribute(new_info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) { info->m_can_write = g_file_info_get_attribute_boolean(new_info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE); } else { info->m_can_write = true; } if (g_file_info_has_attribute(new_info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE)) { info->m_can_excute = g_file_info_get_attribute_boolean(new_info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE); } else { info->m_can_excute = true; } info->m_can_delete = g_file_info_get_attribute_boolean(new_info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE); info->m_can_trash = g_file_info_get_attribute_boolean(new_info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH); info->m_can_rename = g_file_info_get_attribute_boolean(new_info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME); info->m_can_mount = g_file_info_get_attribute_boolean(new_info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT); info->m_can_unmount = g_file_info_get_attribute_boolean(new_info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT); info->m_can_eject = g_file_info_get_attribute_boolean(new_info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_EJECT); info->m_can_start = g_file_info_get_attribute_boolean(new_info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START); info->m_can_stop = g_file_info_get_attribute_boolean(new_info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_STOP); info->m_is_virtual = g_file_info_get_attribute_boolean(new_info, G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL); if(g_file_info_has_attribute(new_info,G_FILE_ATTRIBUTE_MOUNTABLE_UNIX_DEVICE_FILE)) info->m_unix_device_file = g_file_info_get_attribute_string(new_info,G_FILE_ATTRIBUTE_MOUNTABLE_UNIX_DEVICE_FILE); GIcon *g_icon = g_file_info_get_icon (new_info); if (G_IS_ICON(g_icon)) { const gchar* const* icon_names = g_themed_icon_get_names(G_THEMED_ICON (g_icon)); if (icon_names) { auto p = icon_names; while (*p) { QIcon icon = QIcon::fromTheme(*p); if (!icon.isNull()) { info->m_icon_name = QString (*p); break; } else { p++; } } } //g_object_unref(g_icon); } //qDebug()<m_symbolic_icon_name = QString (*symbolic_icon_names); //g_object_unref(g_symbolic_icon); } char* name = g_file_get_path(info->m_file); info->m_path = name; if (NULL != name) { g_free(name); } info->m_file_id = g_file_info_get_attribute_string(new_info, G_FILE_ATTRIBUTE_ID_FILE); info->m_content_type = g_file_info_get_content_type (new_info); if (info->m_content_type == nullptr) { if (g_file_info_has_attribute(new_info, "standard::fast-content-type")) { info->m_content_type = g_file_info_get_attribute_string(new_info, "standard::fast-content-type"); } } info->m_size = g_file_info_get_attribute_uint64(new_info, G_FILE_ATTRIBUTE_STANDARD_SIZE); info->m_modified_time = g_file_info_get_attribute_uint64(new_info, G_FILE_ATTRIBUTE_TIME_MODIFIED); info->m_access_time = g_file_info_get_attribute_uint64(new_info, G_FILE_ATTRIBUTE_TIME_ACCESS); info->m_mime_type_string = info->m_content_type; if (!info->m_mime_type_string.isEmpty()) { char *content_type = g_content_type_get_description (info->m_mime_type_string.toUtf8().constData()); info->m_file_type = content_type; g_free (content_type); content_type = nullptr; } if (info->m_size) { // char *size_full = strtok(g_format_size_full(info->m_size, G_FORMAT_SIZE_IEC_UNITS),"iB"); //列表视图显示改为GB - List view display changed to GB char *size_full = g_format_size_full(info->m_size, G_FORMAT_SIZE_IEC_UNITS); info->m_file_size = QString(size_full).replace("iB", "B");; g_free(size_full); } else { info->m_file_size = nullptr; } auto systemTimeFormat = GlobalSettings::getInstance()->getSystemTimeFormat(); QDateTime date = QDateTime::fromMSecsSinceEpoch(info->m_modified_time*1000); if (info->m_modified_time) { info->m_modified_date = date.toString(systemTimeFormat); } else { info->m_modified_date = nullptr; } if (info->m_access_time) { date = QDateTime::fromMSecsSinceEpoch(info->m_access_time*1000); info->m_access_date = date.toString(systemTimeFormat); } else { info->m_access_date = nullptr; } if (g_file_info_has_attribute(new_info, "trash::deletion-date")) { QString deletionDate = g_file_info_get_attribute_as_string(new_info, G_FILE_ATTRIBUTE_TRASH_DELETION_DATE); info->m_deletion_date = deletionDate.replace("T", " "); QDateTime dateTime = QDateTime::fromString (deletionDate, "yyyy-MM-dd HH:mm:ss"); info->m_deletion_date_uint64 = dateTime.toMSecsSinceEpoch (); } m_info->m_meta_info = FileMetaInfo::fromGFileInfo(m_info->uri(), new_info); // update peony qt color list after meta info updated. m_info->m_colors = FileLabelModel::getGlobalModel()->getFileColors(m_info->uri()); auto customIconName = m_info->m_meta_info.get()->getMetaInfoString("custom-icon"); if (!customIconName.isEmpty() && !customIconName.startsWith("/")) { m_info->m_icon_name = customIconName; } queryFileDisplayName(new_info); info->m_target_uri = g_file_info_get_attribute_string(new_info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); info->m_symlink_target = g_file_info_get_symlink_target(new_info); Q_EMIT info->updated(); // m_info->m_mutex.unlock(); } QString FileInfoJob::getAppName(QString desktopfp) { GError** error=nullptr; GKeyFileFlags flags=G_KEY_FILE_NONE; GKeyFile* keyfile=g_key_file_new (); QByteArray fpbyte=desktopfp.toLocal8Bit(); char* filepath=fpbyte.data(); g_key_file_load_from_file(keyfile,filepath,flags,error); char* name=g_key_file_get_locale_string(keyfile,"Desktop Entry","Name", nullptr, nullptr); QString namestr=QString::fromLocal8Bit(name); g_key_file_free(keyfile); return namestr; } peony/libpeony-qt/controls/0000755000175000017500000000000014205113763014740 5ustar fengfengpeony/libpeony-qt/controls/navigation-bar/0000755000175000017500000000000014205115226017635 5ustar fengfengpeony/libpeony-qt/controls/navigation-bar/navigation-tool-bar.h0000644000175000017500000000345714205101223023663 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef NAVIGATIONTOOLBAR_H #define NAVIGATIONTOOLBAR_H #include #include #include "peony-core_global.h" namespace Peony { class DirectoryViewContainer; class PEONYCORESHARED_EXPORT NavigationToolBar : public QToolBar { Q_OBJECT public: explicit NavigationToolBar(QWidget *parent = nullptr); bool canGoBack(); bool canGoForward(); bool canCdUp(); Q_SIGNALS: void updateWindowLocationRequest(const QString &uri, bool addHistory, bool forceUpdate = false); void refreshRequest(); public Q_SLOTS: void setCurrentContainer(DirectoryViewContainer *container); void updateActions(); void onGoBack(); void onGoForward(); void onGoToUri(const QString &uri, bool addHistory, bool forceUpdate = false); void clearHistory(); private: DirectoryViewContainer *m_current_container = nullptr; QAction *m_back_action; QAction *m_forward_action; QAction *m_history_action; QAction *m_cd_up_action; QAction *m_refresh_action; }; } #endif // NAVIGATIONTOOLBAR_H peony/libpeony-qt/controls/navigation-bar/preview-option-toolbar.cpp0000644000175000017500000000164314205101223024764 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "preview-option-toolbar.h" using namespace Peony; PreviewOptionToolBar::PreviewOptionToolBar(QWidget *parent) : QToolBar(parent) { } peony/libpeony-qt/controls/navigation-bar/advanced-location-bar.h0000644000175000017500000000375714205115226024137 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef ADVANCEDLOCATIONBAR_H #define ADVANCEDLOCATIONBAR_H #include #include "peony-core_global.h" class QStackedLayout; namespace Peony { class LocationBar; class PathEdit; class SearchBarContainer; class PEONYCORESHARED_EXPORT AdvancedLocationBar : public QWidget { Q_OBJECT public: explicit AdvancedLocationBar(QWidget *parent = nullptr); bool isEditing(); Q_SIGNALS: void updateWindowLocationRequest(const QString &uri, bool addHistory = true, bool forceUpdate = false); void refreshRequest(); void searchRequest(const QString &path, const QString &key); void updateFileTypeFilter(const int &index); public Q_SLOTS: void updateLocation(const QString &uri); void startEdit(); void cancelEdit(); void finishEdit(); void switchEditMode(bool bSearchMode); void clearSearchBox(); QString processSpecialChar(QString key); private: QStackedLayout *m_layout; LocationBar *m_bar; PathEdit *m_edit; SearchBarContainer *m_search_bar; QString m_text; QString m_last_non_search_path; QString m_last_key = ""; bool m_in_search_mode = false; const QStringList SPECIAL_CHARS = {"\$", "\|", "\^", "\*"}; }; } #endif // ADVANCEDLOCATIONBAR_H peony/libpeony-qt/controls/navigation-bar/location-bar/0000755000175000017500000000000014205115226022207 5ustar fengfengpeony/libpeony-qt/controls/navigation-bar/location-bar/location-bar.pri0000644000175000017500000000014514205101223025265 0ustar fengfengINCLUDEPATH += $$PWD HEADERS += \ $$PWD/location-bar.h SOURCES += \ $$PWD/location-bar.cpp peony/libpeony-qt/controls/navigation-bar/location-bar/location-bar.cpp0000644000175000017500000004670414205115226025300 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #include "location-bar.h" #include "path-bar-model.h" #include "file-utils.h" #include "search-vfs-uri-parser.h" #include "fm-window.h" #include "file-info.h" #include "file-info-job.h" #include "file-enumerator.h" #include "global-settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Peony; class LocationBarButtonStyle; class IndicatorToolButton; static LocationBarButtonStyle *buttonStyle = nullptr; class LocationBarButtonStyle : public QProxyStyle { public: explicit LocationBarButtonStyle() : QProxyStyle() {} static LocationBarButtonStyle *getStyle() { if (!buttonStyle) { buttonStyle = new LocationBarButtonStyle; } return buttonStyle; } void polish(QWidget *widget) override; void unpolish(QWidget *widget) override; void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget = nullptr) const override; void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const override; }; class IndicatorToolButton : public QToolButton { public: explicit IndicatorToolButton(QWidget *parent = nullptr); protected: void paintEvent(QPaintEvent *) override; }; LocationBar::LocationBar(QWidget *parent) : QWidget(parent) { setAttribute(Qt::WA_Hover); setMouseTracking(true); //comment to fix button text show incomplete issue, link to bug#72080 // setStyleSheet("padding-right: 15;" // "margin-left: 2"); m_styled_edit = new QLineEdit; qDebug()<setObjectName("peony_location_bar_indicator"); m_indicator->setFocusPolicy(Qt::FocusPolicy(m_indicator->focusPolicy() & ~Qt::TabFocus)); m_indicator->setAutoRaise(true); //m_indicator->setStyle(LocationBarButtonStyle::getStyle()); m_indicator->setPopupMode(QToolButton::InstantPopup); m_indicator->setArrowType(Qt::RightArrow); m_indicator->setCheckable(true); m_indicator->setFixedSize(this->height() - 2, this->height() - 2); m_indicator->move(0, 1); m_indicator_menu = new QMenu(m_indicator); m_indicator->setMenu(m_indicator_menu); m_indicator->setArrowType(Qt::RightArrow); connect(m_indicator_menu, &QMenu::aboutToShow, this, [=](){ m_indicator->setArrowType(Qt::DownArrow); }); connect(m_indicator_menu, &QMenu::aboutToHide, this, [=](){ m_indicator->setArrowType(Qt::RightArrow); }); //fix bug 40503, button text not show completely issue when fontsize is very big if (QGSettings::isSchemaInstalled("org.ukui.style")) { //font monitor QGSettings *fontSetting = new QGSettings(FONT_SETTINGS, QByteArray(), this); connect(fontSetting, &QGSettings::changed, this, [=](const QString &key){ if (key == "systemFontSize") { // note that updateButtons() will cost more time. // there is no need to query file infos again here. // use doLayout() is enough. // btw, Bug#76858 is directly caused by info querying // due to updateButtons(). //updateButtons(); doLayout(); } }); } } LocationBar::~LocationBar() { m_styled_edit->deleteLater(); } void LocationBar::setRootUri(const QString &uri) { //when is the same uri and has buttons return if (m_current_uri == uri && m_buttons.count() >0) return; m_current_uri = uri; //clear buttons clearButtons(); if (m_current_uri.startsWith("search://")) { //m_indicator->setArrowType(Qt::NoArrow); addButton(m_current_uri, false, false); return; } m_current_info = FileInfo::fromUri(uri); m_buttons_info.clear(); auto tmpUri = uri; while (!tmpUri.isEmpty() && tmpUri != "") { m_buttons_info.prepend(FileInfo::fromUri(tmpUri)); tmpUri = FileUtils::getParentUri(tmpUri); } m_querying_buttons_info = m_buttons_info; for (auto info : m_buttons_info) { auto infoJob = new FileInfoJob(info); infoJob->setAutoDelete(); connect(infoJob, &FileInfoJob::queryAsyncFinished, this, [=](){ // enumerate buttons info directory auto enumerator = new FileEnumerator; enumerator->setEnumerateDirectory(info.get()->uri()); //comment to fix kydroid path show abnormal issue //enumerator->setEnumerateWithInfoJob(); connect(enumerator, &FileEnumerator::enumerateFinished, this, [=](bool successed){ if (successed) { auto infos = enumerator->getChildren(); m_infos_hash.insert(info.get()->uri(), infos); m_querying_buttons_info.removeOne(info); if (m_querying_buttons_info.isEmpty()) { // add buttons clearButtons(); for (auto info : m_buttons_info) { addButton(info.get()->uri().toLocal8Bit(), true, true); } doLayout(); } } enumerator->deleteLater(); }); enumerator->enumerateAsync(); }); infoJob->queryAsync(); } return; } void LocationBar::clearButtons() { for (auto button : m_buttons) { button->hide(); button->deleteLater(); } m_buttons.clear(); } void LocationBar::updateButtons() { //clear buttons clearButtons(); if (m_current_uri.startsWith("search://")) { //m_indicator->setArrowType(Qt::NoArrow); addButton(m_current_uri, false, false); return; } auto uri = m_current_uri; m_current_info = FileInfo::fromUri(uri); m_buttons_info.clear(); while (!uri.isEmpty() && uri != "") { m_buttons_info.prepend(FileInfo::fromUri(uri)); uri = FileUtils::getParentUri(uri); } m_querying_buttons_info = m_buttons_info; for (auto info : m_buttons_info) { auto infoJob = new FileInfoJob(info); infoJob->setAutoDelete(); connect(infoJob, &FileInfoJob::queryAsyncFinished, this, [=](){ // enumerate buttons info directory auto enumerator = new FileEnumerator; enumerator->setEnumerateDirectory(info.get()->uri()); enumerator->setEnumerateWithInfoJob(); connect(enumerator, &FileEnumerator::enumerateFinished, this, [=](bool successed){ if (successed) { auto infos = enumerator->getChildren(); m_infos_hash.insert(info.get()->uri(), infos); m_querying_buttons_info.removeOne(info); if (m_querying_buttons_info.isEmpty()) { // add buttons clearButtons(); for (auto info : m_buttons_info) { addButton(info.get()->uri(), true, true); } doLayout(); } } enumerator->deleteLater(); }); enumerator->enumerateAsync(); }); infoJob->queryAsync(); } } void LocationBar::addButton(const QString &uri, bool setIcon, bool setMenu) { setIcon = true; QToolButton *button = new QToolButton(this); button->setFocusPolicy(Qt::FocusPolicy(button->focusPolicy() & ~Qt::TabFocus)); button->setAutoRaise(true); button->setStyle(LocationBarButtonStyle::getStyle()); button->setProperty("uri", uri); button->setFixedHeight(this->height()); button->setIconSize(QSize(16, 16)); button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); button->setPopupMode(QToolButton::MenuButtonPopup); auto displayName = FileUtils::getFileDisplayName(uri); button->setToolTip(displayName); m_buttons.insert(QUrl(uri).toEncoded(), button); if (m_current_uri.startsWith("search://")) { QString nameRegexp = SearchVFSUriParser::getSearchUriNameRegexp(m_current_uri); QString targetDirectory = SearchVFSUriParser::getSearchUriTargetDirectory(m_current_uri); button->setIcon(QIcon::fromTheme("edit-find-symbolic")); displayName = tr("Search \"%1\" in \"%2\"").arg(nameRegexp).arg(targetDirectory); button->setText(displayName); button->setFixedWidth(button->sizeHint().width()); return; } QUrl url = uri; auto parent = FileUtils::getParentUri(uri); if (setIcon) { QIcon icon = QIcon::fromTheme(Peony::FileUtils::getFileIconName(uri), QIcon::fromTheme("folder")); button->setIcon(icon); } //comment to fix button text show incomplete issue, link to bug#72080 //button->setStyleSheet("QToolButton{padding-left: 13px; padding-right: 13px}"); if (!url.fileName().isEmpty()) { if (FileUtils::getParentUri(uri).isNull()) { setMenu = false; } 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(); displayName = fontMetrics().elidedText(displayName, Qt::ElideRight, ELIDE_TEXT_LENGTH * charWidth); } button->setText(displayName); connect(button, &QToolButton::clicked, [=]() { Q_EMIT this->groupChangedRequest(uri); }); if (setMenu) { auto infos = m_infos_hash.value(uri); QStringList uris; for (auto info : infos) { if (info.get()->isDir() && !info.get()->displayName().startsWith(".")) uris<uri(); } if (uris.isEmpty()) button->setPopupMode(QToolButton::InstantPopup); Peony::PathBarModel m; m.setStringList(uris); m.sort(0); auto suburis = m.stringList(); if (!suburis.isEmpty()) { QMenu *menu = new QMenu; connect(button, &QToolButton::destroyed, menu, &QMenu::deleteLater); const int WIDTH_EXTEND = 5; connect(menu, &QMenu::aboutToShow, this, [=](){ menu->setMinimumWidth(button->width() + WIDTH_EXTEND); }); QList actions; for (auto uri : suburis) { QString tmp = uri; displayName = Peony::FileUtils::getFileDisplayName(uri); if (displayName.length() > ELIDE_TEXT_LENGTH) { int charWidth = fontMetrics().averageCharWidth(); displayName = fontMetrics().elidedText(displayName, Qt::ElideRight, ELIDE_TEXT_LENGTH * charWidth); } QIcon icon = QIcon::fromTheme(Peony::FileUtils::getFileIconName(uri), QIcon::fromTheme("folder")); QAction *action = new QAction(icon, displayName, this); actions<addActions(actions); button->setMenu(menu); } else { // no subdir directory should not display an indicator arrow. button->setPopupMode(QToolButton::InstantPopup); } } button->setContextMenuPolicy(Qt::CustomContextMenu); connect(button, &QWidget::customContextMenuRequested, this, [=](){ QMenu menu; FMWindowIface *windowIface = dynamic_cast(this->topLevelWidget()); auto copy = menu.addAction(QIcon::fromTheme("edit-copy-symbolic"), tr("Copy Directory")); menu.addAction(QIcon::fromTheme("tab-new-symbolic"), tr("Open In New Tab"), [=](){ windowIface->addNewTabs(QStringList()<create(uri); dynamic_cast(newWindow)->show(); }); if (copy == menu.exec(QCursor::pos())) { if (uri.startsWith("file://")) { QUrl url = uri; QApplication::clipboard()->setText(url.path()); } else { QApplication::clipboard()->setText(uri); } } }); } void LocationBar::mousePressEvent(QMouseEvent *e) { //eat this event. //QToolBar::mousePressEvent(e); if (e->button() == Qt::LeftButton) { Q_EMIT blankClicked(); } } void LocationBar::paintEvent(QPaintEvent *e) { //QToolBar::paintEvent(e); QPainter p(this); QStyleOptionFocusRect opt; opt.initFrom(this); QStyleOptionFrame fopt; fopt.initFrom(this); fopt.state |= QStyle::State_HasFocus; //fopt.state.setFlag(QStyle::State_HasFocus); fopt.rect.adjust(0, 0, 0, 0); fopt.palette.setColor(QPalette::Highlight, fopt.palette.button().color()); fopt.palette.setColor(QPalette::Base, fopt.palette.window().color()); style()->drawPrimitive(QStyle::PE_PanelLineEdit, &fopt, &p, this); style()->drawControl(QStyle::CE_ToolBar, &opt, &p, this); } void LocationBar::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); doLayout(); } void LocationBar::doLayout() { m_indicator->setVisible(false); QList sizeHints; m_indicator_menu->clear(); for (auto button : m_buttons) { button->setVisible(true); button->resize(button->sizeHint().width(), button->height()); button->setToolButtonStyle(Qt::ToolButtonTextOnly); button->adjustSize(); sizeHints<sizeHint().width(); button->setVisible(false); } int totalWidth = this->width(); 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++; currentWidth = tmp; } else { break; } } int offset = 0; bool indicatorVisible = visibleButtonCount < sizeHints.count(); if (indicatorVisible) { m_indicator->setVisible(true); offset += 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->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); button->resize(totalWidth - 20, button->height()); } int spaceCount = 0; QList 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(), nullptr); 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) { if (widget->objectName() == "peony_location_bar_indicator") { return; } QProxyStyle::polish(widget); widget->setProperty("useIconHighlightEffect", true); widget->setProperty("iconHighLightEffectMode", 1); } void LocationBarButtonStyle::unpolish(QWidget *widget) { QProxyStyle::unpolish(widget); widget->setProperty("useIconHighlightEffect", QVariant()); widget->setProperty("iconHighLightEffectMode", QVariant()); } void LocationBarButtonStyle::drawComplexControl(QStyle::ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const { if (control == QStyle::CC_ToolButton) { auto toolButton = qstyleoption_cast(option); auto opt = *toolButton; if (widget && widget->objectName() == "peony_location_bar_indicator") { opt.features.setFlag(QStyleOptionToolButton::HasMenu, false); return QProxyStyle::drawComplexControl(control, &opt, painter); } else { opt.rect.adjust(1, 1, -1, -1); } return QProxyStyle::drawComplexControl(control, &opt, painter, widget); } return QProxyStyle::drawComplexControl(control, option, painter, widget); } void LocationBarButtonStyle::drawControl(QStyle::ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { return QProxyStyle::drawControl(element, option, painter, widget); } IndicatorToolButton::IndicatorToolButton(QWidget *parent) : QToolButton(parent) { } void IndicatorToolButton::paintEvent(QPaintEvent *) { QPainter p(this); QStyleOptionToolButton opt; initStyleOption(&opt); LocationBarButtonStyle::getStyle()->drawComplexControl(QStyle::CC_ToolButton, &opt, &p, this); } peony/libpeony-qt/controls/navigation-bar/location-bar/location-bar.h0000644000175000017500000000420114205101223024717 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef LOCATIONBAR_H #define LOCATIONBAR_H #include #include "peony-core_global.h" #include class QLineEdit; class QHBoxLayout; class QToolButton; class QMenu; namespace Peony { class FileInfo; class LocationBar : public QWidget { Q_OBJECT public: explicit LocationBar(QWidget *parent = nullptr); ~LocationBar() override; const QString getCurentUri() { return m_current_uri; } Q_SIGNALS: void groupChangedRequest(const QString &uri); void blankClicked(); public Q_SLOTS: void setRootUri(const QString &uri); protected: void clearButtons(); void addButton(const QString &uri, bool setIcon = false, bool setMenu = true); void updateButtons(); void mousePressEvent(QMouseEvent *e) override; void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *event) override; void doLayout(); private: QString m_current_uri; QLineEdit *m_styled_edit; QHBoxLayout *m_layout; QMap m_buttons; QToolButton *m_indicator; QMenu *m_indicator_menu; const int ELIDE_TEXT_LENGTH = 16; std::shared_ptr m_current_info; QList> m_buttons_info; QList> m_querying_buttons_info; QHash>> m_infos_hash; }; } #endif // LOCATIONBAR_H peony/libpeony-qt/controls/navigation-bar/navigation-bar.h0000644000175000017500000000373014205101223022702 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef NAVIGATIONBAR_H #define NAVIGATIONBAR_H #include #include "peony-core_global.h" class QMenu; class QActionGroup; namespace Peony { class DirectoryViewContainer; class AdvancedLocationBar; class NavigationToolBar; class NavigationBar : public QToolBar { Q_OBJECT public: explicit NavigationBar(QWidget *parent = nullptr); ~NavigationBar(); bool isPathEditing(); const QString getLastPreviewPageId(); Q_SIGNALS: void updateWindowLocationRequest(const QString &uri, bool addHistory, bool forceUpdate = false); void refreshRequest(); void switchPreviewPageRequest(const QString &id); public Q_SLOTS: void bindContainer(DirectoryViewContainer *container); void updateLocation(const QString &uri); void setBlock(bool block = true); void startEdit(); void finishEdit(); void triggerAction(const QString &id); protected: const QString getCurrentUri(); void paintEvent(QPaintEvent *e); private: NavigationToolBar *m_left_control; AdvancedLocationBar *m_center_control; QActionGroup *m_group; QAction *m_checked_preview_action; QString m_last_preview_page_id_in_window = nullptr; }; } #endif // NAVIGATIONBAR_H peony/libpeony-qt/controls/navigation-bar/navigation-tool-bar.cpp0000644000175000017500000001174014205101223024210 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "navigation-tool-bar.h" #include "file-utils.h" #include #include using namespace Peony; #include "directory-view-container.h" NavigationToolBar::NavigationToolBar(QWidget *parent) : QToolBar(parent) { m_back_action = addAction(QIcon::fromTheme("go-previous"), tr("Go Back"), [=]() { this->onGoBack(); }); m_forward_action = addAction(QIcon::fromTheme("go-next"), tr("Go Forward"), [=]() { this->onGoForward(); }); m_history_action = addAction(QIcon::fromTheme("ukui-down-symbolic", QIcon(":/icons/ukui-down-symbolic")), tr("History")); auto historyButtonWidget = widgetForAction(m_history_action); auto historyButton = qobject_cast(historyButtonWidget); //historyButton->setToolButtonStyle(Qt::ToolButtonFollowStyle); //historyButton->setArrowType(Qt::NoArrow); //historyButton->setPopupMode(QToolButton::DelayedPopup); connect(m_history_action, &QAction::triggered, [=]() { QMenu historyMenu; //historyButton->setMenu(&historyMenu); auto back_list = m_current_container->getBackList(); auto current_uri = m_current_container->getCurrentUri(); auto forward_list = m_current_container->getForwardList(); QStringList historyList; historyList< actions; int count = 0; for (auto uri : historyList) { count++; auto action = historyMenu.addAction(QString::number(count) + ". " + uri); if (historyMenu.actions().indexOf(action) == currentIndex) { action->setCheckable(true); action->setChecked(true); } actions<showMenu(); auto result = historyMenu.exec(historyButtonWidget->mapToGlobal(historyButton->rect().bottomLeft())); int clicked_index = historyMenu.actions().indexOf(result); if (clicked_index == historyMenu.actions().count() - 1) { //FIXME: clear history. m_back_action->setDisabled(true); m_forward_action->setDisabled(true); m_current_container->clearHistory(); } qDebug()<tryJump(clicked_index); //historyButton->setMenu(nullptr); }); m_cd_up_action = addAction(QIcon::fromTheme("go-up"), tr("Cd Up"), [=]() { if (m_current_container) { m_current_container->cdUp(); } }); m_refresh_action = addAction(QIcon::fromTheme("gtk-refresh"), tr("Refresh"), [=]() { Q_EMIT refreshRequest(); }); updateActions(); } void NavigationToolBar::updateActions() { m_back_action->setEnabled(canGoBack()); m_forward_action->setEnabled(canGoForward()); m_cd_up_action->setEnabled(canCdUp()); } bool NavigationToolBar::canCdUp() { if (!m_current_container) return false; return m_current_container->canCdUp(); } bool NavigationToolBar::canGoBack() { if (!m_current_container) return false; return m_current_container->canGoBack(); } bool NavigationToolBar::canGoForward() { if (!m_current_container) return false; return m_current_container->canGoForward(); } void NavigationToolBar::onGoBack() { if (canGoBack()) { m_current_container->goBack(); } } void NavigationToolBar::onGoForward() { if (canGoForward()) { m_current_container->goForward(); } } void NavigationToolBar::onGoToUri(const QString &uri, bool addHistory, bool forceUpdate) { if (m_current_container) { m_current_container->goToUri(uri, addHistory); updateWindowLocationRequest(m_current_container->getCurrentUri(), false, forceUpdate); } } void NavigationToolBar::clearHistory() { } void NavigationToolBar::setCurrentContainer(DirectoryViewContainer *container) { if (m_current_container == container) return; m_current_container = container; Q_EMIT updateWindowLocationRequest(container->getCurrentUri(), false); } peony/libpeony-qt/controls/navigation-bar/advanced-location-bar.cpp0000644000175000017500000001321514205115226024460 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "advanced-location-bar.h" #include "path-edit.h" #include "location-bar.h" #include "search-vfs-uri-parser.h" #include "search-bar-container.h" #include #include using namespace Peony; AdvancedLocationBar::AdvancedLocationBar(QWidget *parent) : QWidget(parent) { QStackedLayout *layout = new QStackedLayout(this); m_layout = layout; layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); layout->setSizeConstraint(QLayout::SetDefaultConstraint); m_bar = new Peony::LocationBar(this); m_edit = new Peony::PathEdit(this); m_search_bar = new Peony::SearchBarContainer(this); m_bar->connect(m_bar, &Peony::LocationBar::blankClicked, [=]() { auto curUri = m_bar->getCurentUri(); if (! curUri.startsWith("trash:///") && ! curUri.startsWith("recent:///")) { layout->setCurrentWidget(m_edit); m_edit->setFocus(); m_edit->setUri(curUri); } }); m_edit->connect(m_edit, &Peony::PathEdit::uriChangeRequest, [=](const QString uri) { //qDebug() << "uriChangeRequest:" <setRootUri(targetUri); layout->setCurrentWidget(m_bar); Q_EMIT this->updateWindowLocationRequest(targetUri); m_text = m_edit->text(); if (! m_text.startsWith("search://")) m_last_non_search_path = m_text; }); m_bar->connect(m_bar, &LocationBar::groupChangedRequest, [=](const QString &uri) { if (m_text == uri) { Q_EMIT this->refreshRequest(); return; } Q_EMIT this->updateWindowLocationRequest(uri); m_text = uri; if (! uri.startsWith("search://")) m_last_non_search_path = uri; }); m_edit->connect(m_edit, &Peony::PathEdit::editCancelled, [=]() { layout->setCurrentWidget(m_bar); }); m_search_bar->connect(m_search_bar, &Peony::SearchBarContainer::returnPressed, [=]() { auto key = m_search_bar->text(); key = processSpecialChar(key); qDebug() << "search key:" <connect(m_search_bar, &Peony::SearchBarContainer::filterUpdate, [=](const int &index) { Q_EMIT this->updateFileTypeFilter(index); }); layout->addWidget(m_bar); layout->addWidget(m_edit); layout->addWidget(m_search_bar); setLayout(layout); setFixedHeight(m_edit->height()); } QString AdvancedLocationBar::processSpecialChar(QString key) { if (key.length() == 0) return key; //qDebug() << "enter processSpecialChar:" <setRootUri(uri); m_edit->setUri(uri); m_text = uri; if (! uri.startsWith("search://")) m_last_non_search_path = uri; Q_EMIT this->refreshRequest(); } bool AdvancedLocationBar::isEditing() { return m_edit->isVisible(); } void AdvancedLocationBar::startEdit() { m_edit->setVisible(true); m_layout->setCurrentWidget(m_edit); m_edit->setFocus(); m_edit->setUri(m_bar->getCurentUri()); } void AdvancedLocationBar::cancelEdit() { m_edit->cancelList(); } void AdvancedLocationBar::finishEdit() { Q_EMIT m_edit->returnPressed(); } 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->setFocus(); m_in_search_mode = true; } else if(m_in_search_mode) { //quit search mode, show non search contents if (m_last_key.length() > 0) { Q_EMIT this->updateWindowLocationRequest(m_last_non_search_path, false); } //在文件保护箱中搜索时,清空搜索内容会导致刷新,路径更新需要授权,导致2次弹框 //屏蔽清空搜索内容代码,解决bug#76431, 概率性闪退问题 QString curUri = m_bar->getCurentUri(); if (! curUri.startsWith("filesafe:///")) m_search_bar->setText(""); m_layout->setCurrentWidget(m_bar); m_in_search_mode = false; } } void AdvancedLocationBar::clearSearchBox() { m_search_bar->clearSearchBox(); m_last_key = ""; } peony/libpeony-qt/controls/navigation-bar/path-bar/0000755000175000017500000000000014205101223021323 5ustar fengfengpeony/libpeony-qt/controls/navigation-bar/path-bar/path-bar.pri0000644000175000017500000000013714205101223023536 0ustar fengfengINCLUDEPATH += $$PWD HEADERS += \ $$PWD/path-edit.h SOURCES += \ $$PWD/path-edit.cpp peony/libpeony-qt/controls/navigation-bar/path-bar/path-edit.cpp0000644000175000017500000001031214205101223023703 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "path-edit.h" #include "path-bar-model.h" #include "path-completer.h" #include "file-utils.h" #include #include #include #include #include #include using namespace Peony; PathEdit::PathEdit(QWidget *parent) : QLineEdit(parent) { setFocusPolicy(Qt::ClickFocus); m_model = new PathBarModel(this); m_completer = new PathCompleter(this); m_completer->setModel(m_model); //m_completer->setCompletionMode(QCompleter::CompletionMode::UnfilteredPopupCompletion); m_completer->popup()->setAttribute(Qt::WA_TranslucentBackground); m_completer->popup()->setProperty("useCustomShadow", true); m_completer->popup()->setProperty("customShadowDarkness", 0.5); m_completer->popup()->setProperty("customShadowWidth", 20); m_completer->popup()->setProperty("customShadowRadius", QVector4D(6, 6, 6, 6)); m_completer->popup()->setProperty("customShadowMargins", QVector4D(20, 20, 20, 20)); m_completer->setCaseSensitivity(Qt::CaseInsensitive); setLayoutDirection(Qt::LeftToRight); QAction *goToAction = new QAction(QIcon::fromTheme("ukui-end-symbolic", QIcon(":/icons/ukui-end-symbolic")), tr("Go To"), this); addAction(goToAction, QLineEdit::TrailingPosition); goToAction->setProperty("isWindowButton", 1); goToAction->setProperty("useIconHighlightEffect", 2); goToAction->setProperty("isIcon", true); connect(goToAction, &QAction::triggered, this, &QLineEdit::returnPressed); setCompleter(m_completer); connect(this, &QLineEdit::returnPressed, [=] { if (this->text().isEmpty()) { this->setText(m_last_uri); this->editCancelled(); return; } else { Q_EMIT this->uriChangeRequest(this->text()); //NOTE: we have send the signal for location change. //so we can use editCancelled hide the path edit. this->editCancelled(); } }); connect(this, &PathEdit::textChanged, this, [=](){ if (!isVisible()) return; auto uri = text(); if (uri.endsWith("/")) { m_model->setRootUri(uri); } // auto parentUri = FileUtils::getParentUri(uri); // if (parentUri == m_model->currentDirUri() && !m_model->stringList().isEmpty()) // return; // if (uri.endsWith("/")) { // //m_model->setRootUri(uri); // } else { // m_model->setRootUri(parentUri); // } }); connect(m_model, &PathBarModel::updated, this, [=](){ if (m_model->stringList().isEmpty()) return; m_completer->complete(); }); } void PathEdit::setUri(const QString &uri) { m_last_uri = uri; setText(QUrl::fromPercentEncoding(m_last_uri.toLocal8Bit())); } void PathEdit::focusOutEvent(QFocusEvent *e) { QLineEdit::focusOutEvent(e); if (! m_right_click) { Q_EMIT editCancelled(); } } void PathEdit::focusInEvent(QFocusEvent *e) { QLineEdit::focusInEvent(e); } void PathEdit::cancelList() { m_completer->activated(m_completer->currentIndex()); } void PathEdit::keyPressEvent(QKeyEvent *e) { QLineEdit::keyPressEvent(e); if (e->key() == Qt::Key_Escape) { Q_EMIT editCancelled(); } } void PathEdit::mousePressEvent(QMouseEvent *e) { QLineEdit::mousePressEvent(e); if (e->button() == Qt::RightButton) m_right_click = true; else m_right_click = false; } peony/libpeony-qt/controls/navigation-bar/path-bar/path-edit.h0000644000175000017500000000307214205101223023355 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef PATHEDIT_H #define PATHEDIT_H #include #include "peony-core_global.h" namespace Peony { class PathBarModel; class PathCompleter; class PEONYCORESHARED_EXPORT PathEdit : public QLineEdit { Q_OBJECT public: explicit PathEdit(QWidget *parent = nullptr); void cancelList(); Q_SIGNALS: void uriChangeRequest(const QString &uri); void editCancelled(); public Q_SLOTS: void setUri(const QString &uri); protected: void focusInEvent(QFocusEvent *e) override; void focusOutEvent(QFocusEvent *e) override; void keyPressEvent(QKeyEvent *e) override; void mousePressEvent(QMouseEvent *e) override; private: QString m_last_uri; PathBarModel *m_model; PathCompleter *m_completer; bool m_right_click = false; }; } #endif // PATHEDIT_H peony/libpeony-qt/controls/navigation-bar/preview-option-toolbar.h0000644000175000017500000000210314205101223024421 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef PREVIEWOPTIONTOOLBAR_H #define PREVIEWOPTIONTOOLBAR_H #include #include "peony-core_global.h" namespace Peony { class PreviewOptionToolBar : public QToolBar { Q_OBJECT public: explicit PreviewOptionToolBar(QWidget *parent = nullptr); }; } #endif // PREVIEWOPTIONTOOLBAR_H peony/libpeony-qt/controls/navigation-bar/navigation-bar.cpp0000644000175000017500000001121714205101223023234 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "navigation-bar.h" #include "navigation-tool-bar.h" #include "advanced-location-bar.h" #include "directory-view-container.h" #include "preview-page-factory-manager.h" #include "preview-page-plugin-iface.h" #include #include #include using namespace Peony; NavigationBar::NavigationBar(QWidget *parent) : QToolBar(parent) { setContentsMargins(0, 0, 5, 0); setFixedHeight(38); setMovable(false); setFloatable(false); m_left_control = new NavigationToolBar(this); m_left_control->setFixedWidth(m_left_control->sizeHint().width()); m_left_control->setContentsMargins(0, 0, 0, 0); addWidget(m_left_control); addSeparator(); m_center_control = new AdvancedLocationBar(this); m_center_control->setContentsMargins(0, 0, 0, 0); addWidget(m_center_control); addSeparator(); connect(m_left_control, &NavigationToolBar::updateWindowLocationRequest, this, &NavigationBar::updateWindowLocationRequest); connect(m_left_control, &NavigationToolBar::refreshRequest, this, &NavigationBar::refreshRequest); connect(m_center_control, &AdvancedLocationBar::updateWindowLocationRequest, this, &NavigationBar::updateWindowLocationRequest); connect(m_center_control, &AdvancedLocationBar::refreshRequest, this, &NavigationBar::refreshRequest); auto manager = PreviewPageFactoryManager::getInstance(); auto ids = manager->getPluginNames(); QActionGroup *group = new QActionGroup(this); m_group = group; group->setExclusive(true); for (auto id : ids) { auto factory = manager->getPlugin(id); auto action = group->addAction(factory->icon(), factory->name()); action->setCheckable(true); connect(action, &QAction::triggered, [=]() { if (m_checked_preview_action == action) { action->setChecked(false); m_checked_preview_action = nullptr; } else { m_checked_preview_action = action; action->setChecked(true); } m_last_preview_page_id_in_window = id; Q_EMIT this->switchPreviewPageRequest(m_checked_preview_action? m_checked_preview_action->text(): nullptr); }); } addActions(group->actions()); } NavigationBar::~NavigationBar() { } void NavigationBar::bindContainer(DirectoryViewContainer *container) { m_left_control->setCurrentContainer(container); m_left_control->updateActions(); updateLocation(container->getCurrentUri()); } void NavigationBar::updateLocation(const QString &uri) { m_center_control->updateLocation(uri); m_left_control->updateActions(); } void NavigationBar::setBlock(bool block) { qDebug()<<"block"<blockSignals(block); m_left_control->blockSignals(block); m_center_control->blockSignals(block); m_left_control->setDisabled(block); m_center_control->setDisabled(block); } void NavigationBar::paintEvent(QPaintEvent *e) { QPainter p(this); //auto color = m_styled_menu->palette().window().color(); auto color = this->palette().base().color(); color.setAlpha(127); p.fillRect(this->rect().adjusted(-1, -1, 1, 1), color); QToolBar::paintEvent(e); } bool NavigationBar::isPathEditing() { return m_center_control->isEditing(); } const QString NavigationBar::getLastPreviewPageId() { if (m_last_preview_page_id_in_window.isNull()) { return PreviewPageFactoryManager::getInstance()->getLastPreviewPageId(); } return m_last_preview_page_id_in_window; } void NavigationBar::startEdit() { m_center_control->startEdit(); } void NavigationBar::finishEdit() { m_center_control->finishEdit(); } void NavigationBar::triggerAction(const QString &id) { for (auto action : m_group->actions()) { if (action->text() == id) { action->trigger(); return; } } } peony/libpeony-qt/controls/navigation-bar/navigation-bar.pri0000644000175000017500000000061414205101223023243 0ustar fengfengINCLUDEPATH += $$PWD include(location-bar/location-bar.pri) include(path-bar/path-bar.pri) HEADERS += \ $$PWD/navigation-tool-bar.h \ $$PWD/advanced-location-bar.h \ $$PWD/navigation-bar.h \ $$PWD/preview-option-toolbar.h SOURCES += \ $$PWD/navigation-tool-bar.cpp \ $$PWD/advanced-location-bar.cpp \ $$PWD/navigation-bar.cpp \ $$PWD/preview-option-toolbar.cpp peony/libpeony-qt/controls/property-page/0000755000175000017500000000000014205115226017532 5ustar fengfengpeony/libpeony-qt/controls/property-page/mark-properties-page-factory.cpp0000644000175000017500000000355314205101223025737 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Wenfei He * */ #include "mark-properties-page-factory.h" #include "mark-properties-page.h" using namespace Peony; static MarkPropertiesPageFactory *global_instance = nullptr; MarkPropertiesPageFactory *MarkPropertiesPageFactory::getInstance() { if (!global_instance) global_instance = new MarkPropertiesPageFactory; return global_instance; } MarkPropertiesPageFactory::MarkPropertiesPageFactory(QObject *parent) : QObject(parent) { } MarkPropertiesPageFactory::~MarkPropertiesPageFactory() { } bool MarkPropertiesPageFactory::supportUris(const QStringList &uris) { //FIXME: 需要明确支持范围 //FIXME: Need to clarify the scope of support if (uris.count() != 1) return false; QString uri = uris.first(); if (uri.startsWith("computer://") || uri.startsWith("recent://") || uri.startsWith("trash://")) return false; return true; } PropertiesWindowTabIface *MarkPropertiesPageFactory::createTabPage(const QStringList &uris) { return new MarkPropertiesPage(uris.first()); } void MarkPropertiesPageFactory::closeFactory() { this->deleteLater(); } peony/libpeony-qt/controls/property-page/open-with-properties-page.cpp0000644000175000017500000003603714205115226025265 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Wenfei He * */ #include "open-with-properties-page.h" #include "file-info.h" #include "file-launch-manager.h" #include "global-settings.h" #include #include #include using namespace Peony; OpenWithPropertiesPage::OpenWithPropertiesPage(const QString &uri, QWidget *parent) : PropertiesWindowTabIface(parent) { m_fileInfo = FileInfo::fromUri(uri); FileInfoJob *job = new FileInfoJob(m_fileInfo); job->setAutoDelete(true); connect(job, &FileInfoJob::queryAsyncFinished, this, &OpenWithPropertiesPage::init); job->queryAsync(); } void OpenWithPropertiesPage::init() { if (m_futureWatcher) { delete m_futureWatcher; m_futureWatcher = nullptr; } m_layout = new QVBoxLayout(this); m_layout->setContentsMargins(0,0,0,0); this->initFloorOne(); this->addSeparator(); this->initFloorTwo(); this->addSeparator(); this->initFloorThree(); this->setLayout(m_layout); } OpenWithPropertiesPage::~OpenWithPropertiesPage() { } void OpenWithPropertiesPage::saveAllChange() { if (!m_thisPageChanged) return; if (m_newAction) { FileLaunchManager::setDefaultLauchAction(m_fileInfo.get()->uri(), m_newAction); } } void OpenWithPropertiesPage::initFloorOne() { QFrame *floor1 = new QFrame(this); QVBoxLayout *layout1 = new QVBoxLayout(this); layout1->setContentsMargins(22,0,22,0); floor1->setLayout(layout1); floor1->setMaximumHeight(142); //这几个组件的最小高度可能让人迷糊,这么设置的原因是因为设计图有一定的区域留空,而label可以自适应高度, //利用这个特性可以将留空的高度全部设置给label使得不用设置margin。(setContentsMargins) QLabel *targetTypeMsgLabel = new QLabel(floor1); targetTypeMsgLabel->setMinimumHeight(60); qDebug() << "targetTypeMsgLabel :" << targetTypeMsgLabel->height(); targetTypeMsgLabel->setText(tr("How do you want to open %1%2 files ?").arg(".").arg(m_fileInfo.get()->displayName().split(".").last())); layout1->addWidget(targetTypeMsgLabel); QLabel *defaultOpenLabel = new QLabel(floor1); defaultOpenLabel->setMinimumHeight(35); defaultOpenLabel->setText(tr("Default open with:")); layout1->addWidget(defaultOpenLabel); m_defaultOpenWithWidget = OpenWithPropertiesPage::createDefaultOpenWithWidget(m_fileInfo->uri(), floor1); m_defaultOpenWithWidget->setMinimumHeight(45); layout1->addWidget(m_defaultOpenWithWidget); m_layout->addWidget(floor1); } void OpenWithPropertiesPage::initFloorTwo() { QFrame *floor2 = new QFrame(this); QVBoxLayout *layout2 = new QVBoxLayout(this); floor2->setLayout(layout2); floor2->setMaximumHeight(226); layout2->setContentsMargins(0,0,0,0); QLabel *otherOpenLabel = new QLabel(floor2); otherOpenLabel->setText(tr("Other:")); otherOpenLabel->setContentsMargins(22,0,0,0); layout2->addWidget(otherOpenLabel); m_launchHashList = new LaunchHashList(m_fileInfo.get()->uri(), floor2); //自定义列表属性 m_launchHashList->m_actionList->setFrameShape(QListWidget::NoFrame); m_launchHashList->m_actionList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // m_launchHashList->m_actionList->setContentsMargins(22,0,0,0); m_launchHashList->m_actionList->setFixedHeight(180); m_launchHashList->m_actionList->setAlternatingRowColors(true); m_launchHashList->m_actionList->setIconSize(QSize(44, 44)); // m_launchHashList->m_actionList->setStyleSheet("QListWidget::Item{padding-left:22px;}"); m_launchHashList->m_actionList->setStyleSheet("QListWidget::Item{margin-left:22px;}"); //m_newAction connect(m_launchHashList->m_actionList, &QListWidget::currentItemChanged, [=](QListWidgetItem *current) { if (!current) return ; FileLaunchAction *action = m_launchHashList->m_actionHash->value(current); if (action == FileLaunchManager::getDefaultAction(m_fileInfo.get()->uri())) return ; this->m_newAction = action; this->thisPageChanged(); }); layout2->addWidget(m_launchHashList->m_actionList); this->m_layout->addWidget(floor2); } void OpenWithPropertiesPage::initFloorThree() { QFrame *floor3 = new QFrame(this); QVBoxLayout *layout3 = new QVBoxLayout(this); floor3->setLayout(layout3); floor3->setMaximumHeight(122); layout3->setContentsMargins(22,0,0,0); QString str1; str1 = "" + tr("Choose other application") + ""; QLabel *allOpenLabel = new QLabel(str1, floor3); allOpenLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); connect(allOpenLabel, &QLabel::linkActivated, this, [=]() { AllFileLaunchDialog dialog(m_fileInfo.get()->uri()); if (QDialog::Accepted == dialog.exec()) { m_defaultOpenWithWidget->setLaunchAction(FileLaunchManager::getDefaultAction(m_fileInfo->uri())); } }); layout3->addWidget(allOpenLabel); QString str2; str2 = "" + tr("Go to application center") + ""; QLabel *otherOpenLabel = new QLabel(str2, floor3); otherOpenLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft); connect(otherOpenLabel, &QLabel::linkActivated, this, [=]() { QtConcurrent::run([=]() { QProcess p; if (COMMERCIAL_VERSION) p.setProgram("kylin-software-center"); else p.setProgram("ubuntu-kylin-software-center"); #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) p.startDetached(); #else p.startDetached("ukui-control-center", QStringList()<<"-a"); #endif }); }); layout3->addWidget(otherOpenLabel); layout3->addStretch(1); this->m_layout->addWidget(floor3); } NewFileLaunchDialog::NewFileLaunchDialog(const QString &uri, QWidget *parent) : QDialog(parent) { m_launchHashList = new LaunchHashList(uri, this); m_layout = new QVBoxLayout(this); setLayout(m_layout); setWindowTitle(tr("Choose new application")); m_layout->addWidget(new QLabel(tr("Choose an Application to open this file"), this)); m_launchHashList->m_actionList->setIconSize(QSize(48, 48)); m_launchHashList->m_actionList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_layout->addWidget(m_launchHashList->m_actionList, 1); m_layout->addWidget(new QLabel(tr("apply now"), this)); m_button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,this); 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")); connect(this, &QDialog::accepted, [=]() { if (!m_launchHashList->m_actionList->currentItem()) return ; FileLaunchAction *action = m_launchHashList->m_actionHash->value(m_launchHashList->m_actionList->currentItem()); if (action) { FileLaunchManager::setDefaultLauchAction(uri, action); } }); connect(m_button_box, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(m_button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); } NewFileLaunchDialog::~NewFileLaunchDialog() { if (m_launchHashList) { delete m_launchHashList; m_launchHashList = nullptr; } } DefaultOpenWithWidget* OpenWithPropertiesPage::createDefaultOpenWithWidget(const QString &uri, QWidget *parent) { DefaultOpenWithWidget* defaultOpenWithWidget = new DefaultOpenWithWidget(parent); defaultOpenWithWidget->setLaunchAction(FileLaunchManager::getDefaultAction(uri)); return defaultOpenWithWidget; } LaunchHashList::LaunchHashList(const QString &uri, QWidget *parent) { m_actionHash = new QHash(); m_actionList = new QListWidget(parent); auto allLaunchActions = FileLaunchManager::getRecommendActions(uri); if (allLaunchActions.count() >= 1) { auto defaultLaunchAction = FileLaunchManager::getDefaultAction(uri); auto defaultItem = new QListWidgetItem(!defaultLaunchAction->icon().isNull()? defaultLaunchAction->icon() : QIcon::fromTheme("application-x-desktop"), defaultLaunchAction->text(), m_actionList); m_actionList->addItem(defaultItem); m_actionList->setCurrentItem(defaultItem); //NOTE:是否需要在列表中显示默认的打开方式 - Do you need to display the default opening method in the list // m_actionList->setItemHidden(defaultItem, true); for (auto action : allLaunchActions) { if (action->getAppInfoDisplayName() == defaultLaunchAction->getAppInfoDisplayName()) continue; auto item = new QListWidgetItem(!action->icon().isNull()? action->icon(): QIcon::fromTheme("application-x-desktop"), action->text(), m_actionList); action->setParent(m_actionList); m_actionList->addItem(item); m_actionHash->insert(item, action); } } } LaunchHashList *LaunchHashList::getAllLaunchHashList(const QString &uri, QWidget *parent) { LaunchHashList *launchHashList = new LaunchHashList(); launchHashList->m_actionHash = new QHash(); launchHashList->m_actionList = new QListWidget(parent); auto allLaunchActions = FileLaunchManager::getAllActions(uri); if (allLaunchActions.count() >= 1) { for (auto action : allLaunchActions) { auto item = new QListWidgetItem(!action->icon().isNull()? action->icon(): QIcon::fromTheme("application-x-desktop"), action->text(), launchHashList->m_actionList); action->setParent(launchHashList->m_actionList); launchHashList->m_actionHash->insert(item, action); launchHashList->m_actionList->addItem(item); } } return launchHashList; } LaunchHashList::LaunchHashList() { } LaunchHashList::~LaunchHashList() { if (m_actionHash) { for (auto key : m_actionHash->keys()) { delete m_actionHash->value(key); } delete m_actionHash; } if (m_actionHash) m_actionList->deleteLater(); } AllFileLaunchDialog::AllFileLaunchDialog(const QString &uri, QWidget *parent) : QDialog(parent) { m_launchHashList = LaunchHashList::getAllLaunchHashList(uri, this); m_layout = new QVBoxLayout(this); setLayout(m_layout); setWindowTitle(tr("Choose new application")); m_layout->addWidget(new QLabel(tr("Choose an Application to open this file"), this)); m_launchHashList->m_actionList->setIconSize(QSize(48, 48)); m_launchHashList->m_actionList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_layout->addWidget(m_launchHashList->m_actionList, 1); m_layout->addWidget(new QLabel(tr("apply now"), this)); m_button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,this); 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")); connect(this, &QDialog::accepted, [=]() { if (!m_launchHashList->m_actionList->currentItem()) return ; FileLaunchAction *action = m_launchHashList->m_actionHash->value(m_launchHashList->m_actionList->currentItem()); if (action) { FileLaunchManager::setDefaultLauchAction(uri, action); } }); connect(m_button_box, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(m_button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); } AllFileLaunchDialog::~AllFileLaunchDialog() { if (m_launchHashList) { delete m_launchHashList; m_launchHashList = nullptr; } } //默认打开方式组件 DefaultOpenWithWidget::DefaultOpenWithWidget(QWidget *parent) : QWidget(parent) { m_appIconLabel = new QLabel(this); m_appNameLabel = new QLabel(this); //设置绝对宽度解决在一定概率下因为图标过大导致appName只剩 ‘...’ 问题 m_appIconLabel->setFixedWidth(32); m_layout = new QHBoxLayout(this); m_layout->setContentsMargins(0, 0, 0, 0); m_layout->setAlignment(Qt::AlignVCenter); m_layout->addWidget(m_appIconLabel, 1); m_layout->addWidget(m_appNameLabel, 9); } void DefaultOpenWithWidget::setAppName(QString appName) { if (appName.isNull()) { this->m_appNameLabel->setText(tr("No default app")); this->m_appIconLabel->setFixedWidth(0); this->m_layout->setSpacing(0); } else { this->m_appNameLabel->setText(appName); this->m_appNameLabel->setToolTip(appName); } } void DefaultOpenWithWidget::setAppIcon(QIcon appIcon) { if (appIcon.isNull()) { this->m_appIconLabel->setPixmap(QIcon::fromTheme("application-x-desktop").pixmap(24, 24)); } else { this->m_appIconLabel->setPixmap(appIcon.pixmap(24, 24)); } } void DefaultOpenWithWidget::resizeEvent(QResizeEvent *event) { //m_appIconLabel->maximumWidth() = 32px; int width = this->width() - m_appIconLabel->maximumWidth(); if (m_appNameLabel->fontMetrics().width(m_appNameLabel->text()) > width) { m_appNameLabel->setText(m_appNameLabel->fontMetrics().elidedText(m_appNameLabel->text(), Qt::ElideRight, width)); } QWidget::resizeEvent(event); } FileLaunchAction* DefaultOpenWithWidget::getLaunchAction() { return m_launchAction; } void DefaultOpenWithWidget::setLaunchAction(FileLaunchAction* launchAction) { if (m_launchAction) { delete m_launchAction; m_launchAction = nullptr; } if (launchAction) { this->m_launchAction = launchAction; this->setAppIcon(m_launchAction->icon()); this->setAppName(m_launchAction->text()); } else { this->setAppIcon(QIcon()); this->setAppName(QString()); } } DefaultOpenWithWidget::~DefaultOpenWithWidget() { if (m_launchAction) { delete m_launchAction; m_launchAction = nullptr; } } peony/libpeony-qt/controls/property-page/details-properties-page-factory.cpp0000644000175000017500000000453014205101223026426 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2021, 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 . * * Authors: Wenfei He * */ #include "details-properties-page-factory.h" #include "details-properties-page.h" #include "file-info.h" using namespace Peony; static DetailsPropertiesPageFactory *global_instance = nullptr; DetailsPropertiesPageFactory *DetailsPropertiesPageFactory::getInstance() { if (!global_instance) global_instance = new DetailsPropertiesPageFactory; return global_instance; } DetailsPropertiesPageFactory::DetailsPropertiesPageFactory() { } DetailsPropertiesPageFactory::~DetailsPropertiesPageFactory() { } bool DetailsPropertiesPageFactory::supportUris(const QStringList &uris) { //FIXME: 需要明确支持范围 //FIXME: Need to clarify the scope of support //只支持文件和应用 //Only supports files and applications if (uris.count() != 1) return false; QString uri = uris.first(); if (uri.startsWith("computer://") || uri.startsWith("recent://") || uri.startsWith("trash://")) return false; //FIXME: 替换ui线程中的阻塞API //FIXME: replace BLOCKING api in ui thread. auto fileInfo = FileInfo::fromUri(uri); FileInfoJob *job = new FileInfoJob(fileInfo); job->setAutoDelete(true); job->querySync(); //排除文件夹和卷 //Exclude folders and volumes if (fileInfo.get()->isDir() || fileInfo.get()->isVolume()) return false; return true; } PropertiesWindowTabIface *DetailsPropertiesPageFactory::createTabPage(const QStringList &uris) { return new DetailsPropertiesPage(uris.first()); } void DetailsPropertiesPageFactory::closeFactory() { this->deleteLater(); } peony/libpeony-qt/controls/property-page/details-properties-page.cpp0000644000175000017500000002570014205115226024773 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2021, 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 . * * Authors: Wenfei He * */ #include "details-properties-page.h" #include "file-info.h" #include "file-utils.h" #include #include #include #include #include #include #include "global-settings.h" #include "file-watcher.h" using namespace Peony; #define FIXED_LABEL_WIDTH 150 //460 - 150 - 22 - 22 = #define FIXED_CONTENT_WIDTH 266 DetailsPropertiesPage::DetailsPropertiesPage(const QString &uri, QWidget *parent) : PropertiesWindowTabIface(parent) { m_uri = uri; m_watcher = std::make_shared(m_uri); m_watcher->startMonitor(); m_layout = new QVBoxLayout(this); m_layout->setContentsMargins(0,10,0,0); m_tableWidget = new QTableWidget(this); this->initTableWidget(); m_layout->addWidget(m_tableWidget); this->setLayout(m_layout); //FIXME: replace BLOCKING api in ui thread. this->getFIleInfo(); this->initDetailsPropertiesPage(); } void DetailsPropertiesPage::getFIleInfo() { m_fileInfo = FileInfo::fromUri(m_uri); FileInfoJob *fileInfoJob = new FileInfoJob(m_fileInfo); fileInfoJob->setAutoDelete(true); fileInfoJob->querySync(); } QLabel *DetailsPropertiesPage::createFixedLabel(quint64 minWidth, quint64 minHeight, QString text, QWidget *parent) { QLabel *l = new QLabel(parent); if(minWidth != 0) l->setMinimumWidth(minWidth); if(minHeight != 0) l->setMinimumHeight(minHeight); l->setText(text); return l; } QWidget *DetailsPropertiesPage::createTableRow(QString labelText, QLabel *contentLabel) { QWidget *row = new QWidget(m_tableWidget); QHBoxLayout *boxLayout = new QHBoxLayout(row); boxLayout->setMargin(0); row->setLayout(boxLayout); QLabel *label1 = this->createFixedLabel(FIXED_LABEL_WIDTH,0,labelText,row); label1->setContentsMargins(22,0,0,0); boxLayout->addWidget(label1); boxLayout->addWidget(contentLabel); boxLayout->addStretch(1); return row; } QWidget *DetailsPropertiesPage::createTableRow(QString labelText, QString content) { QWidget *row = new QWidget(m_tableWidget); QHBoxLayout *boxLayout = new QHBoxLayout(row); boxLayout->setMargin(0); row->setLayout(boxLayout); QLabel *label1 = this->createFixedLabel(FIXED_LABEL_WIDTH,0,labelText,row); label1->setContentsMargins(22,0,0,0); boxLayout->addWidget(label1); boxLayout->addWidget(this->createFixedLabel(0,0,content,row)); boxLayout->addStretch(1); return row; } void DetailsPropertiesPage::addRow(QString labelText, QString content) { int rowCount = m_tableWidget->rowCount(); m_tableWidget->setRowCount( rowCount + 1); m_tableWidget->setCellWidget((rowCount - 1),0,createTableRow(labelText, content)); } void DetailsPropertiesPage::addRow(QString labelText, QLabel *contentLabel) { int rowCount = m_tableWidget->rowCount(); m_tableWidget->setRowCount( rowCount + 1); m_tableWidget->setCellWidget((rowCount - 1),0,createTableRow(labelText, contentLabel)); } void DetailsPropertiesPage::initTableWidget() { m_tableWidget->setColumnCount(1); m_tableWidget->setRowCount(1); m_tableWidget->verticalHeader()->setVisible(false); m_tableWidget->horizontalHeader()->setVisible(false); m_tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); m_tableWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_tableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_tableWidget->setFrameShape(QFrame::NoFrame); m_tableWidget->setSelectionMode(QTableWidget::NoSelection); m_tableWidget->setShowGrid(false); m_tableWidget->setAlternatingRowColors(true); m_tableWidget->rowHeight(36); m_tableWidget->setEditTriggers(QTableWidget::NoEditTriggers); } void DetailsPropertiesPage::initDetailsPropertiesPage() { if (!m_tableWidget) return; QFontMetrics fm = this->fontMetrics(); //name QString fileName = m_fileInfo->displayName(); m_nameLabel = this->createFixedLabel(0, 0, "", m_tableWidget); if (fm.width(fileName) > FIXED_CONTENT_WIDTH) { m_nameLabel->setToolTip(fileName); fileName = m_tableWidget->fontMetrics().elidedText(fileName, Qt::ElideMiddle, FIXED_CONTENT_WIDTH); } m_nameLabel->setText(fileName); this->addRow(tr("Name:"), m_nameLabel); //type this->addRow(tr("File type:"),m_fileInfo->fileType()); //location QUrl url = FileUtils::getParentUri(m_fileInfo->uri()); QString location = url.toDisplayString(); if (location.startsWith("file://")) location = location.split("file://").last(); m_localLabel = this->createFixedLabel(0, 0, "", m_tableWidget); if (fm.width(location) > FIXED_CONTENT_WIDTH) { m_localLabel->setToolTip(location); location = m_tableWidget->fontMetrics().elidedText(location, Qt::ElideMiddle,FIXED_CONTENT_WIDTH); } m_localLabel->setText(location); this->addRow(tr("Location:"), m_localLabel); //createTime if (m_fileInfo->isDir()) { m_createDateLabel = this->createFixedLabel(0,0,"",m_tableWidget); this->addRow(tr("Create time:"),m_createDateLabel); } //modifiedTime m_modifyDateLabel = this->createFixedLabel(0,0,"",m_tableWidget); this->addRow(tr("Modify time:"),m_modifyDateLabel); //default format this->setSystemTimeFormat(tr("yyyy-MM-dd, HH:mm:ss")); // set time connect(GlobalSettings::getInstance(), &GlobalSettings::valueChanged, this, [=] (const QString& key) { if (UKUI_CONTROL_CENTER_PANEL_PLUGIN_TIME == key) { if ("12" == GlobalSettings::getInstance()->getValue(key)) { setSystemTimeFormat(tr("yyyy-MM-dd, hh:mm:ss AP")); } else if ("24" == GlobalSettings::getInstance()->getValue(key)) { setSystemTimeFormat(tr("yyyy-MM-dd, HH:mm:ss")); } updateFileInfo(m_fileInfo.get()->uri()); } }); //size this->addRow(tr("File size:"),m_fileInfo->fileSize()); //判断文件类型 if (m_fileInfo->isImageFile()) { if (m_fileInfo->canRead()) { //image info m_imageWidthLabel = this->createFixedLabel(0, 0, "", m_tableWidget); this->addRow(tr("Width:"), m_imageWidthLabel); m_imageHeightLabel = this->createFixedLabel(0, 0, "", m_tableWidget); this->addRow(tr("Height:"), m_imageHeightLabel); m_imageDepthLabel = this->createFixedLabel(0, 0, "", m_tableWidget); //FIXME:缺少图片位深 // this->addRow(tr("Depth:"),m_imageDepthLabel); } } m_ownerLabel = this->createFixedLabel(0,0,tr("Owner"),m_tableWidget); this->addRow(tr("Owner:"), m_ownerLabel); m_computerLabel = this->createFixedLabel(0,0,tr("Computer"),m_tableWidget); this->addRow(tr("Computer:"), m_computerLabel); m_tableWidget->hideRow(m_tableWidget->rowCount() - 1); this->updateFileInfo(m_uri); connect(m_watcher.get(), &FileWatcher::locationChanged, [=](const QString&, const QString &uri) { this->updateFileInfo(m_uri); }); } DetailsPropertiesPage::~DetailsPropertiesPage() { } void DetailsPropertiesPage::saveAllChange() { if (!m_thisPageChanged) return; } void DetailsPropertiesPage::setSystemTimeFormat(QString format) { this->m_systemTimeFormat = format; } void DetailsPropertiesPage::updateFileInfo(const QString &uri) { this->getFIleInfo(); QUrl url(uri); if(uri.startsWith("filesafe:///")){ QUrl newUrl = QUrl(m_fileInfo->targetUri()); url = newUrl; } QFontMetrics fm = this->fontMetrics(); //FIXME:暂时不处理除了本地文件外的文件信息,希望添加对其他文件的支持 if (url.isLocalFile()) { QString path = url.path(); QFileInfo qFileInfo(path); m_ownerLabel->setText(qFileInfo.owner()); //FIXME:明确当前文件所属计算机 if (qFileInfo.isNativePath()) { QString str_m_computerLabel = tr("%1 (this computer)").arg(QHostInfo::localHostName()); if (fm.width(str_m_computerLabel) > FIXED_CONTENT_WIDTH) { m_computerLabel->setToolTip(str_m_computerLabel); str_m_computerLabel = m_tableWidget->fontMetrics().elidedText(str_m_computerLabel, Qt::ElideMiddle, FIXED_CONTENT_WIDTH); } m_computerLabel->setText(str_m_computerLabel); } else { m_computerLabel->setText(tr("Unknown")); } //FIXME:文件的创建时间会随着文件被修改而发生改变,甚至会出现创建时间晚于修改时间问题 后期将qt的方法替换为gio的方法 //参考:https://www.oschina.net/news/126468/gnome-40-alpha-preview if (qFileInfo.isDir() && m_createDateLabel) { QDateTime date1 = qFileInfo.birthTime(); QString time1 = date1.toString(m_systemTimeFormat); m_createDateLabel->setText(time1); } GFile *file = g_file_new_for_uri(uri.toUtf8().constData()); GFileInfo *info = g_file_query_info(file, "time::*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); g_object_unref(file); quint64 timeNum2 = g_file_info_get_attribute_uint64(info,"time::modified"); QDateTime date2 = QDateTime::fromMSecsSinceEpoch(timeNum2*1000); QString time2 = date2.toString(m_systemTimeFormat); m_modifyDateLabel->setText(time2); g_object_unref(info); } else { if (m_createDateLabel) m_createDateLabel->setText(tr("Can't get remote file information")); m_modifyDateLabel->setText(tr("Can't get remote file information")); } //image file if (m_fileInfo->isImageFile()) { //image width QImageReader r(url.path()); if (m_imageHeightLabel && m_imageWidthLabel && m_imageDepthLabel) { m_imageWidthLabel->setText(tr("%1 px").arg(r.size().width())); m_imageHeightLabel->setText(tr("%1 px").arg(r.size().height())); //FIXME:获取图片文件的位深 // m_imageDepthLabel->setText(32); } } } peony/libpeony-qt/controls/property-page/mark-properties-page-factory.h0000644000175000017500000000411114205101223025373 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Wenfei He * */ #ifndef MARKPROPERTIESPAGEFACTORY_H #define MARKPROPERTIESPAGEFACTORY_H #include #include "peony-core_global.h" #include "properties-window-tab-page-plugin-iface.h" namespace Peony { class MarkPropertiesPageFactory : public QObject, public PropertiesWindowTabPagePluginIface { Q_OBJECT public: static MarkPropertiesPageFactory *getInstance(); //plugin iface const QString name() override { return QObject::tr("Mark"); } PluginType pluginType() override { return PluginType::PropertiesWindowPlugin; } const QString description() override { return QObject::tr("mark this file."); } const QIcon icon() override { return QIcon::fromTheme("view-paged-symbolic", QIcon::fromTheme("folder")); } void setEnable(bool enable) override { Q_UNUSED(enable) } bool isEnable() override { return true; } //properties plugin iface int tabOrder() override { return 900; } bool supportUris(const QStringList &uris) override; PropertiesWindowTabIface *createTabPage(const QStringList &uris) override; void closeFactory() override; private: explicit MarkPropertiesPageFactory(QObject *parent = nullptr); ~MarkPropertiesPageFactory() override; }; } #endif //MARKPROPERTIESPAGEFACTORY_H peony/libpeony-qt/controls/property-page/mark-properties-page.h0000644000175000017500000000356614205101223023743 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Wenfei He * */ #ifndef MARKPROPERTIESPAGE_H #define MARKPROPERTIESPAGE_H #include "properties-window-tab-iface.h" #include "file-label-model.h" #include #include namespace Peony { class MarkPropertiesPage : public PropertiesWindowTabIface { public: /*! * \brief 保存设置 */ void saveAllChange() override; /*! * \brief 初始化表格相关设置 */ void initTableWidget(); /*! * \brief 初始化表格中的数据 */ void initTableData(); void changeLabel(int labelId, bool checked); public: MarkPropertiesPage(const QString &uri, QWidget *parent = nullptr); ~MarkPropertiesPage(); private: QString m_uri; QVBoxLayout *m_layout = nullptr; QTableWidget *m_tableWidget = nullptr; //文件标记模型 FileLabelModel *m_fileLabelModel = nullptr; //当前文件的全部标签 QList m_thisFileLabelIds; /*! * \brief 把color转换为16进制字符串 * \param color * \return */ QString convertRGB16HexStr(const QColor color); }; } #endif //MARKPROPERTIESPAGE_H peony/libpeony-qt/controls/property-page/basic-properties-page-factory.cpp0000644000175000017500000000415414205101223026064 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "basic-properties-page-factory.h" #include "basic-properties-page.h" using namespace Peony; static BasicPropertiesPageFactory *global_instance = nullptr; BasicPropertiesPageFactory *BasicPropertiesPageFactory::getInstance() { if (!global_instance) global_instance = new BasicPropertiesPageFactory; return global_instance; } BasicPropertiesPageFactory::BasicPropertiesPageFactory(QObject *parent) : QObject(parent) { } BasicPropertiesPageFactory::~BasicPropertiesPageFactory() { } bool BasicPropertiesPageFactory::supportUris(const QStringList &uris) { //FIXME: 需要明确支持范围 //FIXME: Need to clarify the scope of support qDebug() << "BasicPropertiesPageFactory::supportUris trace:" << uris.contains("computer:///") << uris; if (uris.contains("computer:///") || uris.contains("recent:///") || uris.contains("trash:///") || uris.contains("network:///")) return false; for (auto uri : uris) { if (uri.startsWith("computer://") || uri.startsWith("trash://") || uri.startsWith("recent://")) return false; } return true; } void BasicPropertiesPageFactory::closeFactory() { this->deleteLater(); } PropertiesWindowTabIface *BasicPropertiesPageFactory::createTabPage(const QStringList &uris) { BasicPropertiesPage *p = new BasicPropertiesPage(uris); return p; } peony/libpeony-qt/controls/property-page/permissions-properties-page-factory.cpp0000644000175000017500000000361114205101223027353 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "permissions-properties-page-factory.h" #include "permissions-properties-page.h" #include using namespace Peony; static PermissionsPropertiesPageFactory *global_instance = nullptr; PermissionsPropertiesPageFactory::PermissionsPropertiesPageFactory(QObject *parent) : QObject(parent) { } bool PermissionsPropertiesPageFactory::supportUris(const QStringList &uris) { //FIXME: 需要明确支持范围 //FIXME: Need to clarify the scope of support if (uris.count() != 1) return false; QString uri = uris.first(); if (uri.startsWith("computer://") || uri.startsWith("recent://") || uri.startsWith("trash://")) return false; return true; } PropertiesWindowTabIface *PermissionsPropertiesPageFactory::createTabPage(const QStringList &uris) { return new PermissionsPropertiesPage(uris); } void PermissionsPropertiesPageFactory::closeFactory() { this->deleteLater(); } PermissionsPropertiesPageFactory *PermissionsPropertiesPageFactory::getInstance() { if (!global_instance) global_instance = new PermissionsPropertiesPageFactory; return global_instance; } peony/libpeony-qt/controls/property-page/computer-properties-page.cpp0000644000175000017500000003001014205115226025172 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "computer-properties-page.h" #include "linux-pwd-helper.h" #include "file-utils.h" #include "datacdrom.h" #include #include #include #include #include #include #include #include #include #include using namespace Peony; ComputerPropertiesPage::ComputerPropertiesPage(const QString &uri, QWidget *parent) : PropertiesWindowTabIface(parent) { m_uri = uri; m_layout = new QFormLayout(this); m_layout->setRowWrapPolicy(QFormLayout::WrapLongRows); m_layout->setFormAlignment(Qt::AlignLeft); m_layout->setLabelAlignment(Qt::AlignRight); setLayout(m_layout); if (uri == "computer:///") { //computer infos QFile cpuInfo("/proc/cpuinfo"); cpuInfo.open(QFile::ReadOnly); QFile memInfo("/proc/meminfo"); memInfo.open(QFile::ReadOnly); auto cpuInfos = cpuInfo.readAll().split('\n'); auto memInfos = memInfo.readAll().split('\n'); QString cpuName; QString cpuCoreCount; QString memSize; for (QString string : cpuInfos) { if (!cpuName.isEmpty() && !cpuCoreCount.isEmpty()) { break; } if (string.startsWith("model name")) { cpuName = string.split(":").last(); } if (string.startsWith("cpu cores")) { cpuCoreCount = string.split(":").last(); } } for (QString string : memInfos) { if (string.startsWith("MemTotal")) { memSize = string.split(":").last(); break; } } memSize.remove(" "); m_layout->addRow(tr("CPU Name:"), new QLabel(cpuName, this)); m_layout->addRow(tr("CPU Core:"), new QLabel(cpuCoreCount, this)); m_layout->addRow(tr("Memory Size:"), new QLabel(" "+memSize, this)); cpuInfo.close(); memInfo.close(); addSeparator(); //user infos auto user = LinuxPWDHelper::getCurrentUser(); QString userName = user.fullName(); userName.replace(',', " "); QString desktopEnv = g_getenv("XDG_CURRENT_DESKTOP"); m_layout->addRow(tr("User Name: "), new QLabel(userName, this)); m_layout->addRow(tr("Desktop: "), new QLabel(desktopEnv, this)); } else { //FIXME: replace BLOCKING api in ui thread. auto targetUri = FileUtils::getTargetUri(uri); if (targetUri.isNull()) { m_layout->addRow(new QLabel(tr("You should mount this volume first"), nullptr)); return; } if (targetUri == "file:///") { //NOTE: file:/// has not mount. GFile *file = g_file_new_for_uri(targetUri.toUtf8().constData()); GFileInfo *info = g_file_query_filesystem_info(file, "*", nullptr, nullptr); quint64 total = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE); quint64 used = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_FILESYSTEM_USED); quint64 available = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE); char *fs_type = g_file_info_get_attribute_as_string(info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE); m_layout->addRow(tr("Name: "), new QLabel(tr("File System"), this)); m_layout->addRow(tr("Total Space: "), new QLabel(formatCapacityString(total), this)); m_layout->addRow(tr("Used Space: "), new QLabel(formatCapacityString(used), this)); m_layout->addRow(tr("Free Space: "), new QLabel(formatCapacityString(available), this)); m_layout->addRow(tr("Type: "), new QLabel(fs_type, this)); g_free(fs_type); g_object_unref(info); g_object_unref(file); auto progressBar = new QProgressBar(this); auto value = double(used*1.0/total)*100; progressBar->setValue(int(value)); m_layout->addRow(progressBar); m_layout->setAlignment(progressBar, Qt::AlignBottom); return; } //FIXME: get volume info correctly. //使用枚举的方法解决空光盘显示未知分区的问题。#58255,#58199 std::shared_ptr volume = ComputerPropertiesPage::EnumerateOneVolumeByTargetUri(targetUri); std::shared_ptr mount = nullptr; if (volume) { mount = std::make_shared(g_volume_get_mount(volume->getGVolume()), true); } else { mount = VolumeManager::getMountFromUri(targetUri); volume = VolumeManager::getVolumeFromMount(mount); } if (mount) { GFile *file = g_file_new_for_uri(targetUri.toUtf8().constData()); GFileInfo *info = g_file_query_filesystem_info(file, "*", nullptr, nullptr); quint64 totalSpace = 0; quint64 usedSpace = 0; quint64 availableSpace = 0; if (info) { quint64 total = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE); quint64 used = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_FILESYSTEM_USED); quint64 free = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE); if (nullptr != volume) { char *deviceName = g_volume_get_identifier(G_VOLUME(volume->getGVolume()), G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); QString unixDeviceName; if(deviceName) { unixDeviceName = QString(deviceName); g_free(deviceName); } //光盘 if (!unixDeviceName.isNull() && !unixDeviceName.isEmpty() && unixDeviceName.startsWith("/dev/sr")) { DataCDROM *cdrom = new DataCDROM(unixDeviceName); if (cdrom) { cdrom->getCDROMInfo(); usedSpace = used; totalSpace = cdrom->getCDROMCapacity(); availableSpace = totalSpace - usedSpace; delete cdrom; cdrom = nullptr; } } } if (totalSpace == 0) { if (total > 0 && (used > 0 || free > 0)) { if (used > 0 && used <= total) { usedSpace = used; totalSpace = total; } else if (free > 0 && free <= total) { usedSpace = total - free; totalSpace = total; } } availableSpace = free; } } char *fs_type = g_file_info_get_attribute_as_string(info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE); //use dubs to get file system type, fix ntfs show as fuse issue QString type = getFileSystemType(uri); if (type.length() <=0) type = fs_type; m_layout->addRow(tr("Name: "), new QLabel(mount->name(), this)); /* if (bMobileDevice) m_layout->addRow(tr("Total Space: "), new QLabel(sizeInfo, this)); else */ m_layout->addRow(tr("Total Space: "), new QLabel(formatCapacityString(totalSpace), this)); m_layout->addRow(tr("Used Space: "), new QLabel(formatCapacityString(usedSpace), this)); m_layout->addRow(tr("Free Space: "), new QLabel(formatCapacityString(availableSpace), this)); m_layout->addRow(tr("Type: "), new QLabel(type, this)); auto progressBar = new QProgressBar(this); auto value = double(usedSpace*1.0/totalSpace)*100; progressBar->setValue(int((value > 0 && value < 1 ) ? 1 : value)); m_layout->addRow(progressBar); m_layout->setAlignment(progressBar, Qt::AlignBottom); if (QString(fs_type) == "isofs" && QFile::exists("/usr/bin/kylin-burner")) { auto pushbutton = new QPushButton(tr("Kylin Burner")); connect(pushbutton, &QPushButton::clicked, pushbutton, [=](){ QProcess p; p.startDetached("kylin-burner"); p.waitForStarted(); }); m_layout->addRow(new QLabel(tr("Open with: \t")), pushbutton); } g_free(fs_type); g_object_unref(info); g_object_unref(file); } else { m_layout->addRow(new QLabel(tr("Unknown"), nullptr)); } } } void ComputerPropertiesPage::addSeparator() { auto separator = new QFrame(this); separator->setFrameShape(QFrame::HLine); m_layout->addRow(separator); } void ComputerPropertiesPage::saveAllChange() { } QString ComputerPropertiesPage::getFileSystemType(QString uri) { QString unixDevice,dbusPath; QString fsType = ""; unixDevice = FileUtils::getUnixDevice(uri); if (unixDevice.isEmpty()) { return fsType; } dbusPath = "/org/freedesktop/UDisks2/block_devices/" + unixDevice.split("/").last(); if (! QDBusConnection::systemBus().isConnected()) return fsType; QDBusInterface blockInterface("org.freedesktop.UDisks2", dbusPath, "org.freedesktop.UDisks2.Block", QDBusConnection::systemBus()); if(blockInterface.isValid()) fsType = blockInterface.property("IdType").toString(); //if need diff FAT16 and FAT32, should use IdVersion // if(fsType == "" && blockInterface.isValid()) // fsType = blockInterface.property("IdVersion").toString(); return fsType; } std::shared_ptr ComputerPropertiesPage::EnumerateOneVolumeByTargetUri(QString targetUri) { std::shared_ptr volume = nullptr; //enumerate auto volume_monitor = g_volume_monitor_get(); auto current_volumes = g_volume_monitor_get_volumes(volume_monitor); GList *l = current_volumes; while (l) { volume = std::make_shared(G_VOLUME(l->data), true); GMount *gMount = g_volume_get_mount(volume->getGVolume()); GFile *gFile = g_mount_get_root(gMount); char *volumePath = g_file_get_uri(gFile); bool isCurrentVolume = (volumePath == targetUri); g_object_unref(gMount); g_object_unref(gFile); g_free(volumePath); if (isCurrentVolume) break; volume = nullptr; l = l->next; } return volume; } QString ComputerPropertiesPage::formatCapacityString(quint64 capacityNum) { // char *strGB = g_format_size_full(capacityNum, G_FORMAT_SIZE_DEFAULT); char *strGiB = g_format_size_full(capacityNum, G_FORMAT_SIZE_IEC_UNITS); // QString formatString(""); // formatString = QString("%1%2%3%4").arg(strGB).arg(" (").arg(strGiB).arg(")"); QString formatString(strGiB); //根据设计要求,按照1024字节对数据进行格式化(1GB = 1024MB),同时将GiB改为GB显示,以便于用户理解。参考windows显示样式。 formatString.replace("iB", "B"); // g_free(strGB); g_free(strGiB); return formatString; } peony/libpeony-qt/controls/property-page/basic-properties-page.h0000644000175000017500000001331414205115226024072 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef BASICPROPERTIESPAGE_H #define BASICPROPERTIESPAGE_H #include #include "peony-core_global.h" #include #include #include #include #include #include #include "properties-window-tab-iface.h" #include "open-with-properties-page.h" #define DEBUG qDebug() << "[" << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << "]" #define CELL1K 1024 #define CELL4K 4096 #define CELL1M 1048576 #define CELL1G 1073741824 class QVBoxLayout; class QFormLayout; class QPushButton; class QLabel; class QLineEdit; namespace Peony { class FileInfo; class FileWatcher; class FileCountOperation; class FileNameThread : public QThread { Q_OBJECT private: const QStringList &m_uris; public: FileNameThread(const QStringList &uris) : m_uris(uris){} Q_SIGNALS: void fileNameReady(QString fileName); protected: void run(); }; /*! * \brief The BasicPropertiesPage class * \todo * handle special files, such as divice or remote server. */ class BasicPropertiesPage : public PropertiesWindowTabIface { Q_OBJECT public: enum FileType{ BP_Folder = 1, BP_File, BP_Application, BP_MultipleFIle /*选中多个文件*/ }; explicit BasicPropertiesPage(const QStringList &uris, QWidget *parent = nullptr); ~BasicPropertiesPage(); void init(); // PropertiesWindowTabIface interface public: void saveAllChange(); protected: void addSeparator(); /*! * * \brief formLayout 左侧label栏 * \param minWidth * \param minHeight * \param text * \param parent * \return */ QLabel *createFixedLabel(quint64 minWidth, quint64 minHeight, QString text, QWidget *parent = nullptr); QLabel *createFixedLabel(quint64 minWidth, quint64 minHeight, QWidget *parent = nullptr); void addOpenWithLayout(QWidget *parent = nullptr); /*! * 初始化第一层显示区域 * \brief * \param uris * \param fileType */ void initFloorOne(const QStringList &uris,BasicPropertiesPage::FileType fileType); void initFloorTwo(const QStringList &uris,BasicPropertiesPage::FileType fileType); void initFloorThree(BasicPropertiesPage::FileType fileType); void initFloorFour(); BasicPropertiesPage::FileType checkFileType(const QStringList &uris); void chooseFileIcon(); void changeFileIcon(); void moveFile(); /** * \brief * \return 如果对名称进行了修改,返回true */ bool isNameChanged(); void setSysTimeFormat(QString format) { this->m_systemTimeFormat = format; } protected Q_SLOTS: void getFIleInfo(QString uri); void onSingleFileChanged(const QString &oldUri, const QString &newUri); void countFilesAsync(const QStringList &uris); void onFileCountOne(const QString &uri, quint64 size); void cancelCount(); void updateInfo(const QString &uri); private: QVBoxLayout *m_layout = nullptr; std::shared_ptr m_info = nullptr; QStringList m_uris; // QFutureWatcher *m_futureWatcher = nullptr; std::shared_ptr m_watcher; std::shared_ptr m_thumbnail_watcher; void updateCountInfo(bool isDone = false); qint64 m_fileDoneCount = 0; qint64 m_labelWidth = 0; //左侧label宽度 //floor1 QPushButton *m_iconButton = nullptr; //文件图标 QString m_newFileIconPath; //文件新图标 //**new version QLineEdit *m_displayNameEdit = nullptr; //文件名称 QLineEdit *m_locationEdit = nullptr; //文件路径 QPushButton *m_moveButton = nullptr; //移动位置按钮 //floor2 -- public QLabel *m_fileTypeLabel = nullptr; //文件类型 QLabel *m_fileSizeLabel = nullptr; //文件大小 QLabel *m_fileTotalSizeLabel = nullptr; //文件占用空间 quint64 m_fileSizeCount = 0; quint64 m_fileTotalSizeCount = 0; //folder type QLabel *m_folderContainLabel = nullptr; //文件夹下文件统计Label quint64 m_folderContainFiles = 0; //文件夹下文件数量 quint64 m_folderContainFolders = 0; //文件夹下文件夹数量 //file , zip QHBoxLayout *m_openWithLayout = nullptr; //文件打开方式 DefaultOpenWithWidget *m_defaultOpenWithWidget = nullptr; //application QLabel *m_descrptionLabel = nullptr; //应用程序描述 //floor3 QString m_systemTimeFormat = ""; QLabel *m_timeCreatedLabel = nullptr; QLabel *m_timeModifiedLabel = nullptr; QLabel *m_timeAccessLabel = nullptr; quint64 m_timeCreated = 0; quint64 m_timeModified = 0; quint64 m_timeAccess = 0; //floor4 QCheckBox *m_readOnly = nullptr; QCheckBox *m_hidden = nullptr; // FileCountOperation *m_countOp = nullptr; }; } #endif // BASICPROPERTIESPAGE_H peony/libpeony-qt/controls/property-page/computer-properties-page.h0000644000175000017500000000375714205101223024651 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef COMPUTERPROPERTIESPAGE_H #define COMPUTERPROPERTIESPAGE_H #include #include "properties-window-tab-iface.h" #include "peony-core_global.h" #include "volume-manager.h" class QFormLayout; namespace Peony { class PEONYCORESHARED_EXPORT ComputerPropertiesPage : public PropertiesWindowTabIface { Q_OBJECT public: explicit ComputerPropertiesPage(const QString &uri, QWidget *parent = nullptr); protected: void addSeparator(); /** * \brief dbus获取文件系统函数 * \param uri * \return */ QString getFileSystemType(QString uri); /*! * \brief 通过给定的targetUri从全部卷中获取匹配的卷。 * \param targetUri 需要匹配的目标Uri (挂载路径) * \return 具有给定targetUri的卷,如果不存在则返回 nullptr */ static std::shared_ptr EnumerateOneVolumeByTargetUri(QString targetUri); /** * \brief 统一格式化容量字符串 * \param capacityNum * \return */ QString formatCapacityString(quint64 capacityNum); private: QString m_uri; QFormLayout *m_layout; // PropertiesWindowTabIface interface public: void saveAllChange(); }; } #endif // COMPUTERPROPERTIESPAGE_H peony/libpeony-qt/controls/property-page/recent-and-trash-properties-page.cpp0000644000175000017500000002002214205115226026475 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "recent-and-trash-properties-page.h" #include "file-info.h" #include "file-info-job.h" #include "file-utils.h" #include "global-settings.h" #include "file-count-operation.h" #include #include #include #include #include #include #include using namespace Peony; //460 - 16 - 16 = 428 #define FIXED_ROW_WIDTH 428; QString RecentAndTrashPropertiesPage::getIconName() { if (m_fileInfo == nullptr) return "application-x-desktop"; QString realPath; bool startWithTrash = m_fileInfo->uri().startsWith("trash:///"); if (startWithTrash) { realPath = QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first() + "/.local/share/Trash/files/" + m_fileInfo->displayName(); } else { realPath = m_fileInfo->targetUri(); } auto _desktop_file = g_desktop_app_info_new_from_filename(QUrl(realPath).path().toUtf8().constData()); if (_desktop_file) { return QString(g_desktop_app_info_get_string(_desktop_file, "Icon")); } //在找不到图标时,返回默认图标 - When the icon is not found, return to the default icon return "application-x-desktop"; } RecentAndTrashPropertiesPage::RecentAndTrashPropertiesPage(const QStringList &uris, QWidget *parent) : PropertiesWindowTabIface(parent) { m_uri = uris.first(); m_fileInfo = FileInfo::fromUri(m_uri); FileInfoJob *job = new FileInfoJob(m_fileInfo); job->setAutoDelete(true); connect(job, &FileInfoJob::queryAsyncFinished, this, &RecentAndTrashPropertiesPage::init); job->queryAsync(); } void RecentAndTrashPropertiesPage::init() { if (m_futureWatcher) { delete m_futureWatcher; m_futureWatcher = nullptr; } m_layout = new QFormLayout(this); m_layout->setRowWrapPolicy(QFormLayout::WrapLongRows); m_layout->setFormAlignment(Qt::AlignLeft|Qt::AlignHCenter); m_layout->setLabelAlignment(Qt::AlignRight|Qt::AlignHCenter); m_layout->setContentsMargins(16,16,16,0); this->setLayout(m_layout); QString iconName = m_fileInfo->iconName(); if (iconName == "application-x-desktop") { iconName = getIconName(); } auto icon = new QPushButton(QIcon::fromTheme(iconName), nullptr, this); icon->setIconSize(QSize(48, 48)); icon->setProperty("isIcon", true); QVBoxLayout *boxLayout = new QVBoxLayout(this); auto name = new QLineEdit(this); name->setReadOnly(true); name->setText(m_fileInfo->displayName()); boxLayout->addWidget(name); boxLayout->setAlignment(Qt::AlignBottom); m_layout->addRow(icon, boxLayout); m_layout->setAlignment(Qt::AlignCenter); addSeparator(); bool startWithTrash = m_uri.startsWith("trash:///"); if (startWithTrash) { if (m_uri == "trash:///") { auto checkbox = new QCheckBox(tr("Show confirm dialog while trashing.")); m_layout->addWidget(checkbox); connect(checkbox, &QCheckBox::toggled, this, [=](bool checked){ this->setProperty("check", checked); }); auto value = GlobalSettings::getInstance()->getValue("showTrashDialog"); if (value.isValid()) { checkbox->setChecked(value.toBool()); } else { checkbox->setChecked(true); } } else { QLabel *label =new QLabel(this); GFile *file = g_file_new_for_uri(m_uri.toUtf8().constData()); GFileInfo *info = g_file_query_info(file, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); auto origin_path = g_file_info_get_attribute_byte_string(info, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH); QUrl url(FileUtils::getParentUri("file://" + QString(origin_path))); quint64 width = FIXED_ROW_WIDTH - label->fontMetrics().width(tr("Origin Path: ")); label->setText(label->fontMetrics().elidedText(url.path(), Qt::ElideMiddle,width)); label->setWordWrap(true); // g_object_unref(info); // g_object_unref(file); m_layout->addRow(tr("Origin Path: "), label); QLabel *size_label =new QLabel(this); if (m_fileInfo->isDir()) { FileCountOperation *fileCountOp = new FileCountOperation(QStringList() << m_fileInfo->uri()); fileCountOp->setAutoDelete(true); connect(fileCountOp, &FileCountOperation::countDone, [=](quint64 file_count, quint64 hidden_file_count, quint64 total_size) { char *fileTotalSizeFormat = g_format_size_full(total_size, G_FORMAT_SIZE_IEC_UNITS); QString fileTotalSizeFormatString(fileTotalSizeFormat); size_label->setText(fileTotalSizeFormatString.replace("iB", "B")); g_free(fileTotalSizeFormat); }); QThreadPool::globalInstance()->start(fileCountOp); } else { size_label->setText(m_fileInfo->fileSize()); } m_layout->addRow(tr("Size: "), size_label); //add delete date label QLabel *delete_label =new QLabel(this); info = g_file_query_info(file, G_FILE_ATTRIBUTE_TRASH_DELETION_DATE, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); QString deletion_date = g_file_info_get_attribute_as_string(info, G_FILE_ATTRIBUTE_TRASH_DELETION_DATE); deletion_date = deletion_date.replace("T", " "); quint64 delete_width = FIXED_ROW_WIDTH - delete_label->fontMetrics().width(tr("Deletion Date: ")); delete_label->setText(label->fontMetrics().elidedText(deletion_date, Qt::ElideMiddle, delete_width)); delete_label->setWordWrap(true); g_object_unref(info); g_object_unref(file); m_layout->addRow(tr("Deletion Date: "), delete_label); } } else { if (m_uri == "recent:///") { } else { QLabel *sizeLabel =new QLabel(this); QLabel *locationLabel =new QLabel(this); auto targetUri = m_fileInfo->targetUri(); quint64 width = FIXED_ROW_WIDTH - locationLabel->fontMetrics().width(tr("Original Location: ")); locationLabel->setText(locationLabel->fontMetrics().elidedText(QUrl(targetUri).path(), Qt::ElideMiddle,width)); locationLabel->setWordWrap(true); sizeLabel->setText(m_fileInfo->fileSize()); m_layout->addRow(tr("Size: "), sizeLabel); m_layout->addRow(tr("Original Location: "), locationLabel); } } } void RecentAndTrashPropertiesPage::addSeparator() { auto separator = new QFrame(this); separator->setFrameShape(QFrame::HLine); m_layout->addRow(separator); } void RecentAndTrashPropertiesPage::saveAllChange() { bool check = this->property("check").toBool(); GlobalSettings::getInstance()->setGSettingValue("showTrashDialog", check); } peony/libpeony-qt/controls/property-page/open-with-properties-page-factory.cpp0000644000175000017500000000445014205101223026714 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Wenfei He * */ #include "open-with-properties-page-factory.h" #include "open-with-properties-page.h" #include "file-info.h" #include "file-info-job.h" using namespace Peony; static OpenWithPropertiesPageFactory *global_instance = nullptr; OpenWithPropertiesPageFactory *OpenWithPropertiesPageFactory::getInstance() { if (!global_instance) global_instance = new OpenWithPropertiesPageFactory; return global_instance; } OpenWithPropertiesPageFactory::OpenWithPropertiesPageFactory(QObject *parent) : QObject(parent) { } OpenWithPropertiesPageFactory::~OpenWithPropertiesPageFactory() { } bool OpenWithPropertiesPageFactory::supportUris(const QStringList &uris) { //FIXME: 需要明确支持范围 //FIXME: Need to clarify the scope of support if (uris.count() != 1) return false; QString uri = uris.first(); if (uri.startsWith("computer://") || uri.startsWith("recent://") || uri.startsWith("trash://")) return false; auto fileInfo = FileInfo::fromUri(uris.first()); FileInfoJob *job = new FileInfoJob(fileInfo); job->setAutoDelete(true); job->querySync(); if (fileInfo.get()->isDir() || fileInfo.get()->isDesktopFile() || fileInfo.get()->isVolume() || fileInfo.get()->isVirtual() || fileInfo->isSymbolLink()) return false; return true; } PropertiesWindowTabIface *OpenWithPropertiesPageFactory::createTabPage(const QStringList &uris) { return new OpenWithPropertiesPage(uris.first()); } void OpenWithPropertiesPageFactory::closeFactory() { this->deleteLater(); } peony/libpeony-qt/controls/property-page/basic-properties-page-factory.h0000644000175000017500000000421514205101223025527 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef BASICPROPERTIESPAGEFACTORY_H #define BASICPROPERTIESPAGEFACTORY_H #include #include "peony-core_global.h" #include "properties-window-tab-page-plugin-iface.h" namespace Peony { class BasicPropertiesPageFactory : public QObject, public PropertiesWindowTabPagePluginIface { Q_OBJECT public: static BasicPropertiesPageFactory *getInstance(); //plugin iface const QString name() override { return QObject::tr("Basic"); } PluginType pluginType() override { return PluginType::PropertiesWindowPlugin; } const QString description() override { return QObject::tr("Show the basic file properties, and allow you to modify the access and name."); } const QIcon icon() override { return QIcon::fromTheme("view-paged-symbolic", QIcon::fromTheme("folder")); } void setEnable(bool enable) override { Q_UNUSED(enable) } bool isEnable() override { return true; } //properties plugin iface int tabOrder() override { return 1000; } bool supportUris(const QStringList &uris) override; PropertiesWindowTabIface *createTabPage(const QStringList &uris) override; void closeFactory() override; private: explicit BasicPropertiesPageFactory(QObject *parent = nullptr); ~BasicPropertiesPageFactory() override; }; } #endif // BASICPROPERTIESPAGEFACTORY_H peony/libpeony-qt/controls/property-page/permissions-properties-page.h0000644000175000017500000000525214205115226025366 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef PERMISSIONSPROPERTIESPAGE_H #define PERMISSIONSPROPERTIESPAGE_H #include #include #include "properties-window-tab-iface.h" #include "peony-core_global.h" #include #include class QLabel; class QTableWidget; class QCheckBox; namespace Peony { class FileWatcher; class PEONYCORESHARED_EXPORT PermissionsPropertiesPage : public PropertiesWindowTabIface { Q_OBJECT public: explicit PermissionsPropertiesPage(const QStringList &uris, QWidget *parent = nullptr); ~PermissionsPropertiesPage(); /*! * init the main Widget * \brief initTabWidget */ void initTableWidget(); /** * \brief 创建一个自定义的表格单元组件,带图标和文字 * \param parent * \param icon * \param text * \return */ static QWidget* createCellWidget(QWidget* parent,QIcon icon, QString text); void savePermissions(); void updateCheckBox(); protected: static GAsyncReadyCallback async_query_permisson_callback(GObject *obj, GAsyncResult *res, PermissionsPropertiesPage *p_this); void queryPermissionsAsync(const QString&, const QString &uri); protected Q_SLOTS: void changePermission(int row, int column, bool checked); Q_SIGNALS: void checkBoxChanged(int row, int column, bool checked); private: QString m_uri; std::shared_ptr m_watcher; QVBoxLayout *m_layout = nullptr; QLabel *m_label = nullptr; QLabel *m_message = nullptr; QTableWidget *m_table = nullptr; //防止错误修改权限 bool m_enable = false; bool m_permissions[3][3]; //unixmode能力,标识修改权限是否可使用gio接口 bool m_has_unix_mode = false; public: void thisPageChanged() override; // PropertiesWindowTabIface interface public: void saveAllChange(); }; } #endif // PERMISSIONSPROPERTIESPAGE_H peony/libpeony-qt/controls/property-page/basic-properties-page.cpp0000644000175000017500000010747314205115226024437 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2021, 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 . * * Authors: Yue Lan * */ #include "basic-properties-page.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "file-info.h" #include "file-info-job.h" #include "file-utils.h" #include "thumbnail-manager.h" #include "file-operation-utils.h" #include "file-watcher.h" #include "file-count-operation.h" #include "file-operation-manager.h" #include "file-meta-info.h" #include "global-settings.h" #include "generic-thumbnailer.h" #include "file-operation-manager.h" #include "open-with-properties-page.h" using namespace Peony; BasicPropertiesPage::BasicPropertiesPage(const QStringList &uris, QWidget *parent) : PropertiesWindowTabIface(parent) { m_uris = uris; //getFIleInfo this->getFIleInfo(m_uris.first()); this->init(); // QFuture future = QtConcurrent::run([=]() { // getFIleInfo(m_uris.first()); // }); // // m_futureWatcher = new QFutureWatcher; // m_futureWatcher->setFuture(future); // // connect(m_futureWatcher,&QFutureWatcher::finished,this,&BasicPropertiesPage::init); } void BasicPropertiesPage::init() { // if (m_futureWatcher) { // delete m_futureWatcher; // m_futureWatcher = nullptr; // } //Time Modified: (是左侧显示最长的字符串) m_labelWidth = fontMetrics().width(tr("Time Modified:")) + 5; m_labelWidth = m_labelWidth < 90 ? 90 : m_labelWidth; //check file type and search fileinfo FileType fileType = this->checkFileType(m_uris); //单个文件才能换icon if (fileType != BP_MultipleFIle) { m_watcher = std::make_shared(m_uris.first()); m_watcher->connect(m_watcher.get(), &FileWatcher::locationChanged, this, &BasicPropertiesPage::onSingleFileChanged); m_watcher->startMonitor(); m_thumbnail_watcher = std::make_shared("thumbnail:///"); connect(m_thumbnail_watcher.get(), &FileWatcher::fileChanged, this, [=](const QString &uri){ auto icon = ThumbnailManager::getInstance()->tryGetThumbnail(uri); m_iconButton->setIcon(icon); //QMessageBox::information(0, 0, "icon updated"); }); } m_layout = new QVBoxLayout(this); m_layout->setMargin(0); m_layout->setSpacing(0); this->setLayout(m_layout); this->initFloorOne(m_uris,fileType); this->addSeparator(); this->initFloorTwo(m_uris,fileType); this->addSeparator(); if(fileType != BP_MultipleFIle ) { this->initFloorThree(fileType); this->addSeparator(); this->initFloorFour(); } m_layout->addStretch(1); } BasicPropertiesPage::~BasicPropertiesPage() { disconnect(); cancelCount(); } void BasicPropertiesPage::addSeparator() { QFrame *separator = new QFrame(this); separator->setFrameShape(QFrame::HLine); m_layout->addWidget(separator); } void BasicPropertiesPage::addOpenWithLayout(QWidget *parent) { if (m_openWithLayout) { m_defaultOpenWithWidget = OpenWithPropertiesPage::createDefaultOpenWithWidget(m_info->uri(), parent); m_openWithLayout->setContentsMargins(0,0,16,0); m_openWithLayout->setAlignment(Qt::AlignVCenter); m_openWithLayout->addWidget(m_defaultOpenWithWidget); m_openWithLayout->addStretch(1); QPushButton *moreAppButton = new QPushButton(parent); moreAppButton->setText(tr("Change")); moreAppButton->setMinimumSize((moreAppButton->fontMetrics().width(tr("Change")) + 5), 30); m_openWithLayout->addWidget(moreAppButton); connect(moreAppButton,&QPushButton::clicked,this,[=](){ NewFileLaunchDialog dialog(m_info.get()->uri()); if (QDialog::Accepted == dialog.exec()) { m_defaultOpenWithWidget->setLaunchAction(FileLaunchManager::getDefaultAction(m_info->uri())); } }); } } /*! * * \brief BasicPropertiesPage::initFloorOne */ void BasicPropertiesPage::initFloorOne(const QStringList &uris,BasicPropertiesPage::FileType fileType) { QFrame *floor1 = new QFrame(this); QGridLayout *layout1 = new QGridLayout(floor1); layout1->setContentsMargins(22,16,0,16); floor1->setMinimumHeight(100); floor1->setLayout(layout1); m_iconButton = new QPushButton(floor1); m_displayNameEdit = new QLineEdit(floor1); m_locationEdit = new QLineEdit(floor1); m_iconButton->setFixedSize(QSize(60,60)); m_iconButton->setIconSize(QSize(48,48)); m_iconButton->setProperty("isIcon", true); auto form1 = new QFormLayout(floor1); layout1->addLayout(form1,0,0); form1->setContentsMargins(0,0,30,0); form1->setFormAlignment(Qt::AlignVCenter); form1->addRow(m_iconButton); //icon area if (fileType != BP_MultipleFIle) { connect(m_iconButton, &QPushButton::clicked, this, &BasicPropertiesPage::chooseFileIcon); this->onSingleFileChanged(nullptr, uris.first()); } else { m_iconButton->setIcon(QIcon::fromTheme("text-x-generic")); } //text area auto form2 = new QFormLayout(floor1); layout1->addLayout(form2, 0, 1); m_displayNameEdit->setMinimumHeight(32); m_locationEdit->setMinimumHeight(32); form2->addRow(tr("Name"), m_displayNameEdit); form2->addRow(tr("Location"), m_locationEdit); if(uris.first() == "filesafe:///"){ form2->itemAt(1,QFormLayout::LabelRole)->widget()->setEnabled(false); } //select multiplefiles if (fileType == BP_MultipleFIle || !m_info->canRename()) m_displayNameEdit->setReadOnly(true); //选中多个文件时,使用另外的线程获取文件名称并拼接 if (fileType == BP_MultipleFIle) { FileNameThread *getNameThread = new FileNameThread(uris); getNameThread->start(); connect(getNameThread, &FileNameThread::fileNameReady, this, [=](QString fileName) { m_displayNameEdit->setText(fileName); delete getNameThread; }); } connect(m_displayNameEdit, &QLineEdit::textChanged, [=]() { if (isNameChanged()) { this->thisPageChanged(); } }); m_locationEdit->setReadOnly(true); QUrl url = FileUtils::getParentUri(uris.first()); QString location = url.toDisplayString(); if (location.startsWith("file://")) location = location.split("file://").last(); m_locationEdit->setText(location); //move -button if(fileType == BP_Folder) { m_moveButton = new QPushButton(floor1); auto form3 = new QFormLayout(floor1); form3->setContentsMargins(0,0,16,0); form3->setFormAlignment(Qt::AlignBottom); m_moveButton->setText(tr("move")); m_moveButton->setMinimumSize(70,32); //暂时使用设置最大高度的方式解决与输入框自适应不一致问题 m_moveButton->setMaximumSize(70, 38); form3->addRow(m_moveButton); //home目录不支持移动和重命名 //不能改名的文件自然不能移动 标准位置文件夹也不能移动 if(m_info.get()->uri() == ("file://"+QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first()) || !m_info->canRename() || FileUtils::isStandardPath(m_info->uri())) { m_displayNameEdit->setReadOnly(true); m_moveButton->setDisabled(true); } connect(m_moveButton,&QPushButton::clicked,this,&BasicPropertiesPage::moveFile); layout1->addLayout(form3,0,2); } else { layout1->setContentsMargins(22,16,16,16); } QString fileUri = uris.at(0); //filesafe插件中保护箱下目录不能移动——fix bug 76864 //fix show properties in filesafe path crash issue, link to bug#74350 if(fileUri.startsWith("filesafe:///") && m_moveButton) { m_moveButton->setVisible(false); } if(fileUri.startsWith("filesafe:///") && (fileUri.remove("filesafe:///").indexOf("/") == -1)) { disconnect(m_iconButton, &QPushButton::clicked, this, &BasicPropertiesPage::chooseFileIcon); m_displayNameEdit->setReadOnly(true); } //add floor1 to context m_layout->addWidget(floor1); } void BasicPropertiesPage::initFloorTwo(const QStringList &uris,BasicPropertiesPage::FileType fileType) { QFrame *floor2 = new QFrame(this); QFormLayout *layout2 = new QFormLayout(floor2); layout2->setContentsMargins(22,16,0,16); layout2->setVerticalSpacing(8); floor2->setLayout(layout2); //144px 为多选文件情况下占用的最大高度 - 144px is the maximum height occupied in the case of multiple selection files floor2->setMinimumHeight(144); m_fileTypeLabel = this->createFixedLabel(0,32,floor2); m_fileSizeLabel = this->createFixedLabel(0,32,floor2); m_fileTotalSizeLabel = this->createFixedLabel(0,32,floor2); layout2->addRow(tr("Type:"),m_fileTypeLabel); if(fileType != BP_MultipleFIle) { //FIX:目前只是在这里对文件快捷方式进行判断并显示,这并不合适,希望在底层文件fileType直接增加快捷方式。 //FIX:At present, only the file shortcuts are judged and displayed here. This is not appropriate. //I hope to directly add shortcuts to the underlying file file Type. m_fileTypeLabel->setText(m_info->isSymbolLink() ? tr("symbolLink") : m_info.get()->fileType()); } if(fileType == BP_Folder && uris.first() == "filesafe:///"){ m_fileTypeLabel->setText(tr("Folder")); } //根据文件类型添加组件 - Add components based on file type switch (fileType) { case BP_Folder: m_folderContainLabel = this->createFixedLabel(0,32,floor2); layout2->addRow(this->createFixedLabel(m_labelWidth,32,tr("Include:"),floor2),m_folderContainLabel); break; case BP_File: m_openWithLayout = new QHBoxLayout(floor2); layout2->addRow(this->createFixedLabel(m_labelWidth,32,tr("Open with:"),floor2),m_openWithLayout); this->addOpenWithLayout(floor2); break; case BP_Application: { m_descrptionLabel = this->createFixedLabel(0,32,floor2); layout2->addRow(this->createFixedLabel(m_labelWidth,32,tr("Description:"),floor2),m_descrptionLabel); //fix bug#53504, not show duplicated name issue QString displayName = m_info.get()->displayName(); if (m_info->isDesktopFile()) { displayName = FileUtils::handleDesktopFileName(m_info->uri(), displayName); } m_descrptionLabel->setText(displayName); } break; case BP_MultipleFIle: m_fileTypeLabel->setText(tr("Select multiple files")); default: break; } layout2->addRow(this->createFixedLabel(m_labelWidth,32,tr("Size:"),floor2),m_fileSizeLabel); layout2->addRow(this->createFixedLabel(m_labelWidth,32,tr("Space Useage:"),floor2),m_fileTotalSizeLabel); this->countFilesAsync(uris); m_layout->addWidget(floor2); } void BasicPropertiesPage::initFloorThree(BasicPropertiesPage::FileType fileType) { this->setSysTimeFormat(tr("yyyy-MM-dd, HH:mm:ss")); // set time connect(GlobalSettings::getInstance(), &GlobalSettings::valueChanged, this, [=] (const QString& key) { if (UKUI_CONTROL_CENTER_PANEL_PLUGIN_TIME == key) { if ("12" == GlobalSettings::getInstance()->getValue(key)) { setSysTimeFormat(tr("yyyy-MM-dd, hh:mm:ss AP")); } else if ("24" == GlobalSettings::getInstance()->getValue(key)) { setSysTimeFormat(tr("yyyy-MM-dd, HH:mm:ss")); } updateInfo(m_info.get()->uri()); } }); auto floor3 = new QFrame(this); QFormLayout *layout3 = new QFormLayout(floor3); layout3->setVerticalSpacing(8); floor3->setLayout(layout3); floor3->setMinimumHeight(64); layout3->setContentsMargins(22,16,0,16); switch (fileType) { case BP_File: case BP_Application: m_timeModifiedLabel = this->createFixedLabel(0,32,floor3); m_timeAccessLabel = this->createFixedLabel(0,32,floor3); layout3->addRow(this->createFixedLabel(m_labelWidth,32,tr("Time Modified:"),floor3), m_timeModifiedLabel); layout3->addRow(this->createFixedLabel(m_labelWidth,32,tr("Time Access:"),floor3), m_timeAccessLabel); break; case BP_MultipleFIle: case BP_Folder: m_timeCreatedLabel = this->createFixedLabel(0,32,floor3); layout3->addRow(this->createFixedLabel(m_labelWidth,32,tr("Time Modified:"),floor3), m_timeCreatedLabel); default: break; } m_layout->addWidget(floor3); updateInfo(m_info.get()->uri()); connect(m_watcher.get(), &FileWatcher::locationChanged, [=](const QString&, const QString &uri) { this->updateInfo(uri); }); } /*! * 底部隐藏多选框和只读选择框 * \brief BasicPropertiesPage::initFloorFour */ void BasicPropertiesPage::initFloorFour() { QFrame *floor4 = new QFrame(this); QFormLayout *layout4 = new QFormLayout(floor4); floor4->setMaximumHeight(64); layout4->setContentsMargins(22,16,0,16); m_readOnly = new QCheckBox(tr("Readonly"), floor4); m_hidden = new QCheckBox(tr("Hidden"), floor4); QHBoxLayout *hBoxLayout = new QHBoxLayout(floor4); hBoxLayout->addWidget(m_readOnly); hBoxLayout->addStretch(1); hBoxLayout->addWidget(m_hidden); hBoxLayout->addStretch(2); if(m_info.get()->canRead() && !m_info.get()->canWrite()) m_readOnly->setCheckState(Qt::Checked); if(m_info.get()->displayName().startsWith(".")) m_hidden->setCheckState(Qt::Checked); m_readOnly->setDisabled(!m_info->canRename()); m_hidden->setDisabled(!m_info->canRename()); layout4->addRow(this->createFixedLabel(m_labelWidth,32,tr("Property:"),floor4),hBoxLayout); //确认被修改 connect(m_readOnly,&QCheckBox::stateChanged,this,&BasicPropertiesPage::thisPageChanged); connect(m_hidden,&QCheckBox::stateChanged,this,&BasicPropertiesPage::thisPageChanged); m_layout->addWidget(floor4); } BasicPropertiesPage::FileType BasicPropertiesPage::checkFileType(const QStringList &uris) { if(uris.count() != 1) { return BP_MultipleFIle; } else { if (m_info->displayName().isNull() || m_info->displayName().isEmpty()) { this->getFIleInfo(uris.first()); } if(m_info.get()->isDir()) return BP_Folder; if(m_info.get()->isDesktopFile()) return BP_Application; return BP_File; } } //异步获取数据 void BasicPropertiesPage::getFIleInfo(QString uri) { //将在收藏夹的文件路径替换为真实路径 - Replace the file path in the favorites with the real path //FIX:如果是远程文件夹或者其它非本地文件夹添加到收藏夹呢? - What if remote folders or other non-local folders are added to favorites? if (uri.startsWith("favorite://")) { QUrl url(uri); uri = "file://" + url.path(); m_uris.clear(); m_uris.append(uri); } std::shared_ptr fileInfo = FileInfo::fromUri(uri); FileInfoJob *fileInfoJob = new FileInfoJob(fileInfo); fileInfoJob->setAutoDelete(); fileInfoJob->querySync(); m_info = fileInfo; } QIcon generateThumbnail(const QString &uri) { QUrl url = uri; if (! uri.startsWith("file:///")) { url = FileUtils::getTargetUri(uri); } auto _desktop_file = g_desktop_app_info_new_from_filename(url.path().toUtf8().constData()); auto _icon_string = g_desktop_app_info_get_string(_desktop_file, "Icon"); QIcon thumbnail = QIcon::fromTheme(_icon_string); QString string = _icon_string; if (thumbnail.isNull() && string.startsWith("/")) { thumbnail = GenericThumbnailer::generateThumbnail(_icon_string, true); } DEBUG << "thumbnail" << thumbnail <<_icon_string; g_free(_icon_string); g_object_unref(_desktop_file); return thumbnail; } void BasicPropertiesPage::onSingleFileChanged(const QString &oldUri, const QString &newUri) { this->getFIleInfo(newUri); ThumbnailManager::getInstance()->createThumbnail(m_info.get()->uri(), m_thumbnail_watcher); auto icon = QIcon::fromTheme(m_info.get()->iconName(), QIcon::fromTheme("text-x-generic")); auto thumbnail = ThumbnailManager::getInstance()->tryGetThumbnail(m_info.get()->uri()); m_iconButton->setIcon(thumbnail.isNull() ? icon : thumbnail); //fix bug#53504, not show duplicated name issue. QString fileName = m_info->displayName(); if (m_info->isDesktopFile() && !fileName.endsWith(".desktop")) { fileName = FileUtils::handleDesktopFileName(m_info->uri(), fileName); } m_displayNameEdit->setText(fileName); if (thumbnail.isNull()) { ThumbnailManager::getInstance()->createThumbnail(m_info.get()->uri(), m_thumbnail_watcher); } m_iconButton->setIcon(thumbnail.isNull() ? icon : thumbnail); QUrl url = FileUtils::getParentUri(m_info.get()->uri()); QString location = url.toDisplayString(); if (location.startsWith("file://")) location = location.split("file://").last(); m_locationEdit->setText(location); } void BasicPropertiesPage::countFilesAsync(const QStringList &uris) { //old op will delete later if (m_countOp) { m_countOp->disconnect(); m_countOp->cancel(); } //clear old data m_folderContainFiles = 0; m_folderContainFolders = 0; m_fileSizeCount = 0; m_fileTotalSizeCount = 0; if (uris.count() == 1) { auto uri = uris.first(); auto info = FileInfo::fromUri(uri); QStringList realUris; //判断是不是快捷方式 - Determine if it is a shortcut if (info->isSymbolLink()) { if (!info.get()->symlinkTarget().isEmpty()) realUris << ("file://" + info.get()->symlinkTarget()); } m_countOp = new FileCountOperation(realUris.isEmpty() ? uris : realUris); DEBUG << "symlinkTarget:" << info.get()->symlinkTarget(); } else { m_countOp = new FileCountOperation(uris); } m_countOp->setAutoDelete(true); connect(m_countOp, &FileOperation::operationPreparedOne, this, &BasicPropertiesPage::onFileCountOne, Qt::BlockingQueuedConnection); connect(m_countOp, &FileCountOperation::countDone, [=](quint64 file_count, quint64 hidden_file_count, quint64 total_size) { m_countOp = nullptr; m_folderContainFiles = file_count - m_folderContainFolders; m_fileSizeCount = total_size; //不统计文件夹本身 - Do not count the folder itself if (m_folderContainFolders != 0) { m_folderContainFolders--; } this->updateCountInfo(true); //使用du -s 命令查看文件实际占用的磁盘空间。 for (QString uri : m_uris) { QUrl url(uri); //某些带空格的文件名称会导致命令错误,加上引号解决此问题。 QString path; if(uri == "filesafe:///") { path = QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/.box"; } else { path = QString("%1%2%3").arg("\"").arg(url.path()).arg("\""); } QProcess process; process.start("du -s " + path); process.waitForFinished(); QString result = process.readAllStandardOutput(); //du -s xxx 输出格式:4 xxx (大小单位为KB) m_fileTotalSizeCount += result.split(QRegExp("\\s+")).first().toLong(); } //转换为 xx Bytes m_fileTotalSizeCount *= CELL1K; if (m_fileTotalSizeCount == 0) { quint64 a = 0; quint64 b = 0; a = m_fileSizeCount % CELL4K; b = m_fileSizeCount / CELL4K; quint64 cell4k = (a == 0) ? b : (b + 1); m_fileTotalSizeCount = cell4k * CELL4K; } //gio格式化工具 char *fileTotalSizeFormat = g_format_size_full(m_fileTotalSizeCount,G_FORMAT_SIZE_IEC_UNITS); QString fileTotalSizeFormatString(fileTotalSizeFormat); fileTotalSizeFormatString.replace("iB", "B"); QString fileTotalSizeText(tr("%1 (%2 Bytes)").arg(fileTotalSizeFormatString).arg(m_fileTotalSizeCount)); g_free(fileTotalSizeFormat); m_fileTotalSizeLabel->setText(fileTotalSizeText); }); QThreadPool::globalInstance()->start(m_countOp); } void BasicPropertiesPage::onFileCountOne(const QString &uri, quint64 size) { //...FIX:第一版本在当前位置对文件夹进行统计,慢 std::shared_ptr l_fileInfo = FileInfo::fromUri(uri); FileInfoJob *fileInfoJob = new FileInfoJob(l_fileInfo); fileInfoJob->setAutoDelete(); fileInfoJob->querySync(); bool a = l_fileInfo.get()->isDir(); if (a) m_folderContainFolders ++; else m_folderContainFiles ++; m_fileSizeCount += size; m_fileDoneCount ++; //500 files update ui updateCountInfo((m_fileDoneCount % 500 == 0)); } void BasicPropertiesPage::cancelCount() { if (m_countOp) m_countOp->cancel(); } void BasicPropertiesPage::moveFile(){ auto newDirPath = QString(QFileDialog::getExistingDirectoryUrl(nullptr, tr("Choose a new folder:"),m_info.get()->uri()).toEncoded()); DEBUG << "new path:" << newDirPath ; if(newDirPath.isEmpty()) return; if(newDirPath == m_info.get()->uri()) { QMessageBox::critical(nullptr, tr("Error"), tr("cannot move a folder to itself !"), QMessageBox::Yes, QMessageBox::Yes); return; } if(newDirPath.startsWith(m_info.get()->uri())) { QMessageBox::critical(nullptr, tr("Error"), tr("cannot move a folder to itself !"), QMessageBox::Yes, QMessageBox::Yes); return; } DEBUG << "old Path:" << m_info.get()->uri() << newDirPath; QStringList uriList; uriList << m_info.get()->uri(); auto fileOpe = FileOperationUtils::move(uriList,newDirPath,true); connect(fileOpe,&FileOperation::operationFinished,[=](){ if (!fileOpe->hasError()) { QProcess p; #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) p.setProgram("peony"); p.setArguments(QStringList()<<"--show-folders" << newDirPath); p.startDetached(); #else p.startDetached("peony", QStringList()<<"--show-folders"<< newDirPath); #endif Q_EMIT requestCloseMainWindow(); } }); } /*! * 响应窗口确定按钮 * \brief BasicPropertiesPage::saveAllChange */ void BasicPropertiesPage::saveAllChange() { if (m_watcher) m_watcher->stopMonitor(); if (m_thumbnail_watcher) m_thumbnail_watcher->stopMonitor(); //未发生修改 if (!this->m_thisPageChanged) return; //拒绝修改home目录 if (m_info.get()->uri() == ("file://"+QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first())) { return; } //修改图标 this->changeFileIcon(); if (m_readOnly) { mode_t mod = 0; if(m_readOnly->isChecked()) { mod |= S_IRUSR; mod |= S_IRGRP; mod |= S_IROTH; } 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); } //是否进行文件隐藏操作 - 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 (isNameChanged()) { newName = m_displayNameEdit->text(); } bool isHidden = m_info.get()->displayName().startsWith("."); //以前没隐藏,并且选中隐藏框 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; } } if (!existHiddenOpt) { if (isNameChanged()) { FileOperationUtils::rename(m_info.get()->uri(), m_displayNameEdit->text(), true); } } } void BasicPropertiesPage::chooseFileIcon() { QUrl iconPathUrl; iconPathUrl.setPath("/usr/share/icons"); auto picture = QFileDialog::getOpenFileName(nullptr, tr("Choose a custom icon"), "/usr/share/icons", "*.png *.jpg *.jpeg *.svg"); if (!picture.isEmpty()) { qDebug()<<"chose new file icon:"<< picture; m_iconButton->setIcon(QIcon(picture)); this->m_newFileIconPath = picture; this->thisPageChanged(); } } void BasicPropertiesPage::changeFileIcon() { if (!m_newFileIconPath.isEmpty()) { auto metaInfo = FileMetaInfo::fromUri(m_info.get()->uri()); QFileInfo fileInfo(m_newFileIconPath); if (!QIcon::fromTheme(fileInfo.baseName()).isNull()) metaInfo.get()->setMetaInfoString("custom-icon", fileInfo.baseName()); else metaInfo.get()->setMetaInfoString("custom-icon", m_newFileIconPath); ThumbnailManager::getInstance()->createThumbnail(m_info.get()->uri(), m_thumbnail_watcher, true); } } void BasicPropertiesPage::updateCountInfo(bool isDone) { if (isDone) { QString fileSizeText; quint64 a = 0; a = m_fileSizeCount / CELL1K; //小于1KB if (a < 1) { fileSizeText = tr("%1 Bytes").arg(m_fileSizeCount); } else { char *fileSizeFormat = g_format_size_full(m_fileSizeCount,G_FORMAT_SIZE_IEC_UNITS); QString fileSizeFormatString(fileSizeFormat); //根据设计要求,按照1024字节对数据进行格式化(1GB = 1024MB),同时将GiB改为GB显示,以便于用户理解。参考windows显示样式。 fileSizeFormatString.replace("iB", "B"); fileSizeText = tr("%1 (%2 Bytes)").arg(fileSizeFormatString).arg(m_fileSizeCount); g_free(fileSizeFormat); } m_fileSizeLabel->setText(fileSizeText); //在为完成统计前,先显示文件大小而不是占用空间大小 m_fileTotalSizeLabel->setText(fileSizeText); if(m_folderContainLabel) m_folderContainLabel->setText(tr("%1 files, %2 folders").arg(m_folderContainFiles).arg(m_folderContainFolders)); } } void BasicPropertiesPage::updateInfo(const QString &uri) { //QT获取文件相关时间 , m_info = FileInfo::fromUri(uri); FileInfoJob *fileInfoJob = new FileInfoJob(m_info); fileInfoJob->setAutoDelete(); connect(fileInfoJob, &FileInfoJob::queryAsyncFinished, this, [=](){ QUrl url(uri); //FIXME:暂时不处理除了本地文件外的文件信息,希望添加对其他文件的支持 //if (url.isLocalFile()) { if(m_info->accessTime() != 0 && m_info->modifiedTime() != 0){ // if(m_timeModifiedLabel) { // QDateTime date2 = qFileInfo.lastModified(); // QString time2 = date2.toString(m_systemTimeFormat); // m_timeModifiedLabel->setText(time2); // } // if(m_timeAccessLabel) { // QDateTime date3 = qFileInfo.lastRead(); // QString time3 = date3.toString(m_systemTimeFormat); // m_timeAccessLabel->setText(time3); // } GFile *file = g_file_new_for_uri(uri.toUtf8().constData()); GFileInfo *info = g_file_query_info(file, "time::*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); g_object_unref(file); m_timeModified = g_file_info_get_attribute_uint64(info,"time::modified"); if(m_timeModifiedLabel) { QDateTime date2 = QDateTime::fromMSecsSinceEpoch(m_timeModified*1000); QString time2 = date2.toString(m_systemTimeFormat); m_timeModifiedLabel->setText(time2); } if(m_timeAccessLabel) { m_timeAccess = g_file_info_get_attribute_uint64(info,"time::access"); QDateTime date3 = QDateTime::fromMSecsSinceEpoch(m_timeAccess*1000); QString time3 = date3.toString(m_systemTimeFormat); m_timeAccessLabel->setText(time3); } QString path = url.path(); QFileInfo qFileInfo(path); if (/*qFileInfo.isDir() && */m_timeCreatedLabel) { m_timeCreated = g_file_info_get_attribute_uint64(info, "time::created"); // 客户需要必须显示创建时间,因此使用三个时间最小时间戳为创建时间 quint64 minTime = m_timeCreated != 0 ? m_timeCreated : m_timeModified; minTime = qMin (minTime, m_timeModified); if (m_timeAccess != 0) minTime = qMin (minTime, m_timeAccess); m_timeCreated = minTime; QDateTime createDate = QDateTime::fromMSecsSinceEpoch(m_timeCreated*1000); QString createTime = createDate.toString(m_systemTimeFormat); m_timeCreatedLabel->setText(createTime); // // FIXME:目前只是文件夹显示创建时间,当创建时间获取失败的时候,将修改时间作为创建时间 // QDateTime date1 = qFileInfo.birthTime(); // if (date1.isValid()) { // QString time1 = date1.toString(m_systemTimeFormat); // m_timeCreatedLabel->setText(time1); // } else { // m_timeCreated = g_file_info_get_attribute_uint64(info, "time::modified"); // QDateTime createDate = QDateTime::fromMSecsSinceEpoch(m_timeCreated*1000); // QString createTime = createDate.toString(m_systemTimeFormat); // m_timeCreatedLabel->setText(createTime); // } } g_object_unref(info); } else { if (m_timeCreatedLabel){ QFontMetrics fontWidth(m_timeCreatedLabel->font()); QString elideNote = fontWidth.elidedText(tr("Can't get remote file information"),Qt::ElideRight,260); m_timeCreatedLabel->setText(elideNote); m_timeCreatedLabel->setToolTip(tr("Can't get remote file information")); } } //FIXME:GVFS底层暂未实现文件创建时间获取API,暂时使用QT获取文件创建时间 /* m_timeCreated = g_file_info_get_attribute_uint64(info,G_FILE_ATTRIBUTE_TIME_CREATED); //m_timeCreated = g_file_info_get_attribute_uint64(info, "time::created"); QDateTime date1 = QDateTime::fromMSecsSinceEpoch(m_timeCreated*1000); QString time1 = date1.toString(m_systemTimeFormat); m_timeCreatedLabel->setText(time1); if(m_timeModifiedLabel) { m_timeModified = g_file_info_get_attribute_uint64(info, "time::modified"); QDateTime date2 = QDateTime::fromMSecsSinceEpoch(m_timeModified*1000); QString time2 = date2.toString(m_systemTimeFormat); m_timeModifiedLabel->setText(time2); } if(m_timeAccessLabel) { m_timeAccess = g_file_info_get_attribute_uint64(info, "time::access"); QDateTime date3 = QDateTime::fromMSecsSinceEpoch(m_timeAccess*1000); QString time3 = date3.toString(m_systemTimeFormat); m_timeAccessLabel->setText(time3); } g_object_unref(info); */ }); fileInfoJob->queryAsync(); } QLabel *BasicPropertiesPage::createFixedLabel(quint64 minWidth, quint64 minHeight, QString text, QWidget *parent) { QLabel *label = new QLabel(parent); if (minWidth != 0) label->setMinimumWidth(minWidth); if (minHeight != 0) label->setMinimumHeight(minHeight); label->setText(text); return label; } QLabel *BasicPropertiesPage::createFixedLabel(quint64 minWidth, quint64 minHeight, QWidget *parent) { QLabel *label = new QLabel(parent); if (minWidth != 0) label->setMinimumWidth(minWidth); if (minHeight != 0) label->setMinimumHeight(minHeight); return label; } bool BasicPropertiesPage::isNameChanged() { if (!m_displayNameEdit->isReadOnly() && !m_displayNameEdit->text().isEmpty()) { QString fileName(m_info->displayName()); //桌面文件 if (m_info->isDesktopFile() && !fileName.endsWith(".desktop")) { //做过处理的名称 QString handledName = FileUtils::handleDesktopFileName(m_info->uri(), fileName); if (fileName != handledName) { //用户是否手动修改 if (handledName != m_displayNameEdit->text()) return true; else return false; } } //文件名称被修改过 if (fileName != m_displayNameEdit->text()) return true; else return false; } return false; } void FileNameThread::run() { QString fileName = ""; if (m_uris.count() == 1) { std::shared_ptr fileInfo = FileInfo::fromUri(m_uris.first()); FileInfoJob *fileInfoJob = new FileInfoJob(fileInfo); fileInfoJob->setAutoDelete(); fileInfoJob->querySync(); fileName = fileInfo.get()->displayName(); //fix bug#53504, not show duplicated name issue if (fileInfo->isDesktopFile()) { fileName = FileUtils::handleDesktopFileName(fileInfo->uri(), fileName); } } else { QStringList stringList; for (auto uri : m_uris) { //FIXME: replace BLOCKING api in ui thread.(finish) ** std::shared_ptr fileInfo = FileInfo::fromUri(uri); FileInfoJob *fileInfoJob = new FileInfoJob(fileInfo); fileInfoJob->setAutoDelete(); fileInfoJob->querySync(); stringList<< fileInfo.get()->displayName(); } auto text = stringList.join(","); fileName = QString(text); } Q_EMIT fileNameReady(fileName); } peony/libpeony-qt/controls/property-page/mark-properties-page.cpp0000644000175000017500000001360514205101223024271 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Wenfei He * */ #include "mark-properties-page.h" #include "file-operation-utils.h" #include #include #include #include #include #include using namespace Peony; MarkPropertiesPage::MarkPropertiesPage(const QString &uri, QWidget *parent) : PropertiesWindowTabIface(parent) { this->m_uri = uri; //note:请查看:BasicPropertiesPage::getFIleInfo(QString uri) - Look BasicPropertiesPage::getFIleInfo(QString uri) if (uri.startsWith("favorite://")) { QUrl url(uri); m_uri = "file://" + url.path(); } this->m_layout = new QVBoxLayout(this); //表格自带一部分高度,所以手动删减一部分 m_layout->setContentsMargins(16,16,16,20); this->initTableWidget(); this->initTableData(); this->setLayout(m_layout); } void MarkPropertiesPage::initTableWidget() { this->m_tableWidget = new QTableWidget(this); m_tableWidget->setContentsMargins(0,0,0,0); m_tableWidget->setRowCount(4); m_tableWidget->setColumnCount(2); m_tableWidget->setSelectionMode(QTableWidget::NoSelection); m_tableWidget->setFrameShape(QFrame::NoFrame); m_tableWidget->horizontalHeader()->setVisible(false); m_tableWidget->verticalHeader()->setVisible(false); m_tableWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); //显示线条 m_tableWidget->setShowGrid(false); m_tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); m_tableWidget->resizeRowsToContents(); //隔行换色 m_tableWidget->setAlternatingRowColors(false); m_layout->addWidget(m_tableWidget); } MarkPropertiesPage::~MarkPropertiesPage() { } void MarkPropertiesPage::saveAllChange() { if(!this->m_thisPageChanged) return; //1.清除旧的标记 for (auto labelId : m_fileLabelModel->getFileLabelIds(m_uri)) { this->m_fileLabelModel->removeFileLabel(m_uri,labelId); } //2.写入新的标记 for(auto labelId : this->m_thisFileLabelIds) { m_fileLabelModel->addLabelToFile(m_uri,labelId); } } void MarkPropertiesPage::initTableData() { m_fileLabelModel = FileLabelModel::getGlobalModel(); m_thisFileLabelIds = m_fileLabelModel->getFileLabelIds(m_uri); QList allLabels = m_fileLabelModel->getAllFileLabelItems(); int rowCount = (allLabels.count()%2 == 0 ? allLabels.count()/2 : allLabels.count()/2 + 1); m_tableWidget->setRowCount(rowCount); int totalLabel = allLabels.count()%2 ? allLabels.count()+1 : allLabels.count(); for (int i = 0; i < totalLabel; i++) { QWidget *widget = new QWidget(m_tableWidget); QHBoxLayout *boxLayout = new QHBoxLayout(m_tableWidget); boxLayout->setAlignment(Qt::AlignLeft); widget->setLayout(boxLayout); //fix last single box can input letters issue, bug#38757 if (i >= allLabels.count()) { QLabel *label = new QLabel(widget); label->setText(""); boxLayout->addWidget(label); m_tableWidget->setCellWidget(i/2,i%2,widget); continue; } auto item = allLabels.at(i); QCheckBox *checkBox = new QCheckBox(widget); checkBox->setChecked(m_thisFileLabelIds.contains(item->id())); boxLayout->addWidget(checkBox); QPushButton *button = new QPushButton(widget); button->palette().window(); button->setStyleSheet("QPushButton{" "border-radius: 6px; " "background-color: " + convertRGB16HexStr(item->color()) + ";" " max-width:12px;" " max-height:12px;" " min-width:12px;" " min-height:12px;" "}"); button->setEnabled(false); boxLayout->addWidget(button); QLabel *label = new QLabel(widget); label->setText(item->name()); boxLayout->addWidget(label); connect(checkBox,&QCheckBox::clicked,this,[=](bool checked){ this->changeLabel(item->id(),checked); }); m_tableWidget->setCellWidget(i/2,i%2,widget); } m_layout->addWidget(m_tableWidget); } void MarkPropertiesPage::changeLabel(int labelId, bool checked) { qDebug() << "id" << labelId << checked; //1.检查是否存在 bool hasLabelId = this->m_thisFileLabelIds.contains(labelId); //2.如果选中并且不存在 if(checked && !hasLabelId) this->m_thisFileLabelIds.append(labelId); //3.如果取消选中,并且存在 if(!checked && hasLabelId) this->m_thisFileLabelIds.removeAt(this->m_thisFileLabelIds.indexOf(labelId)); this->thisPageChanged(); } QString MarkPropertiesPage::convertRGB16HexStr(const QColor color) { if (color == Qt::transparent) return "transparent"; QString redStr = QString("%1").arg(color.red(),2,16,QChar('0')); QString greenStr = QString("%1").arg(color.green(),2,16,QChar('0')); QString blueStr = QString("%1").arg(color.blue(),2,16,QChar('0')); QString hexStr = "#" + redStr + greenStr + blueStr; return hexStr; } peony/libpeony-qt/controls/property-page/details-properties-page-factory.h0000644000175000017500000000403514205101223026073 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2021, 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 . * * Authors: Wenfei He * */ #ifndef PEONY_DETAILS_PROPERTIES_PAGE_FACTORY_H #define PEONY_DETAILS_PROPERTIES_PAGE_FACTORY_H #include "properties-window-tab-page-plugin-iface.h" namespace Peony { class DetailsPropertiesPageFactory : public QObject, public PropertiesWindowTabPagePluginIface { Q_OBJECT public: static DetailsPropertiesPageFactory *getInstance(); //plugin iface const QString name() override { return QObject::tr("Details"); } PluginType pluginType() override { return PluginType::PropertiesWindowPlugin; } const QString description() override { return QObject::tr("Details"); } const QIcon icon() override { return QIcon::fromTheme("view-paged-symbolic", QIcon::fromTheme("folder")); } void setEnable(bool enable) override { Q_UNUSED(enable) } bool isEnable() override { return true; } //properties plugin iface int tabOrder() override { return 600; } bool supportUris(const QStringList &uris) override; PropertiesWindowTabIface *createTabPage(const QStringList &uris) override; void closeFactory() override; private: explicit DetailsPropertiesPageFactory(); ~DetailsPropertiesPageFactory(); }; } #endif //PEONY_DETAILS_PROPERTIES_PAGE_FACTORY_H peony/libpeony-qt/controls/property-page/property-page.pri0000644000175000017500000000241514205101223023036 0ustar fengfengINCLUDEPATH += $$PWD HEADERS += \ $$PWD/basic-properties-page.h \ $$PWD/basic-properties-page-factory.h \ $$PWD/permissions-properties-page-factory.h \ $$PWD/permissions-properties-page.h \ $$PWD/computer-properties-page-factory.h \ $$PWD/computer-properties-page.h \ $$PWD/recent-and-trash-properties-page-factory.h \ $$PWD/recent-and-trash-properties-page.h \ $$PWD/mark-properties-page-factory.h \ $$PWD/mark-properties-page.h \ $$PWD/open-with-properties-page-factory.h \ $$PWD/open-with-properties-page.h \ $$PWD/details-properties-page-factory.h \ $$PWD/details-properties-page.h SOURCES += \ $$PWD/basic-properties-page.cpp \ $$PWD/basic-properties-page-factory.cpp \ $$PWD/permissions-properties-page-factory.cpp \ $$PWD/permissions-properties-page.cpp \ $$PWD/computer-properties-page-factory.cpp \ $$PWD/computer-properties-page.cpp \ $$PWD/recent-and-trash-properties-page-factory.cpp \ $$PWD/recent-and-trash-properties-page.cpp \ $$PWD/mark-properties-page-factory.cpp \ $$PWD/mark-properties-page.cpp \ $$PWD/open-with-properties-page-factory.cpp \ $$PWD/open-with-properties-page.cpp \ $$PWD/details-properties-page-factory.cpp \ $$PWD/details-properties-page.cpp peony/libpeony-qt/controls/property-page/permissions-properties-page.cpp0000644000175000017500000004644014205115226025725 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "permissions-properties-page.h" #include "linux-pwd-helper.h" #include "file-watcher.h" #include "file-info.h" #include "file-info-job.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Peony; #define OWNER 0 #define GROUP 1 #define OTHERS 2 #define USER 0 #define READABLE 2 #define WRITEABLE 3 #define EXECUTEABLE 4 //460 - 22 - 22 = 416 ,右侧有字符被遮挡,再减去6px -_-; #define TARGET_LABEL_WIDTH 410 PermissionsPropertiesPage::PermissionsPropertiesPage(const QStringList &uris, QWidget *parent) : PropertiesWindowTabIface(parent) { m_uri = uris.first(); QUrl url(m_uri); //note:请查看:BasicPropertiesPage::getFIleInfo(QString uri) - Look BasicPropertiesPage::getFIleInfo(QString uri) if (m_uri.startsWith("favorite://")) { m_uri = "file://" + url.path(); url = QUrl(m_uri); } m_layout = new QVBoxLayout(this); m_layout->setContentsMargins(0,0,0,0); this->setLayout(m_layout); m_label = new QLabel(this); QString str = tr("Target: %1").arg(url.path()); int fontSize = m_label->fontMetrics().width(str); if(fontSize > TARGET_LABEL_WIDTH) { m_label->setToolTip(str); str = m_label->fontMetrics().elidedText(str, Qt::ElideMiddle, TARGET_LABEL_WIDTH); } m_label->setText(str); m_label->setMinimumHeight(60); m_label->setContentsMargins(22,0,22,0); m_layout->addWidget(m_label); m_message = new QLabel(this); m_message->setAlignment(Qt::AlignCenter); this->initTableWidget(); m_layout->addWidget(m_message); m_message->setVisible(false); m_watcher = std::make_shared(m_uri); connect(m_watcher.get(), &FileWatcher::locationChanged, this, &PermissionsPropertiesPage::queryPermissionsAsync); connect(this, &PermissionsPropertiesPage::checkBoxChanged, this, &PermissionsPropertiesPage::changePermission); queryPermissionsAsync(nullptr, m_uri); } PermissionsPropertiesPage::~PermissionsPropertiesPage() { } void PermissionsPropertiesPage::initTableWidget() { m_table = new QTableWidget(this); m_table->setRowCount(4); m_table->setColumnCount(5); m_table->verticalHeader()->setVisible(false); m_table->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_table->horizontalHeader()->setFrameShape(QFrame::NoFrame); m_table->setFrameShape(QFrame::NoFrame); m_table->horizontalHeader()->setSelectionMode(QTableWidget::NoSelection); m_table->setSelectionMode(QTableWidget::NoSelection); m_table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); m_table->setShowGrid(false); m_table->horizontalHeader()->setMinimumHeight(34); m_table->rowHeight(34); m_table->setAlternatingRowColors(true); auto l = QStringList(); l<setHorizontalHeaderLabels(l); m_table->setEditTriggers(QTableWidget::NoEditTriggers); //开启手动设置宽度 - Enable manual width setting m_table->horizontalHeader()->setMinimumSectionSize(30); m_table->horizontalHeader()->setMaximumSectionSize(400); m_table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Interactive); m_table->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Interactive); m_table->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch); m_table->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch); m_table->horizontalHeader()->setSectionResizeMode(4, QHeaderView::Stretch); m_table->horizontalHeaderItem(0)->setTextAlignment(Qt::AlignLeft); m_table->setColumnWidth(0, 150); m_layout->addWidget(m_table); } void PermissionsPropertiesPage::queryPermissionsAsync(const QString &, const QString &uri) { m_uri = uri; QUrl url = m_uri; m_label->setText(m_label->fontMetrics().elidedText(tr("Target: %1").arg(url.path()), Qt::ElideMiddle,TARGET_LABEL_WIDTH)); m_table->setEnabled(false); GFile *file = g_file_new_for_uri(m_uri.toUtf8().constData()); g_file_query_info_async(file, "owner::*," "access::*," "unix::mode", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, 0, nullptr, GAsyncReadyCallback(async_query_permisson_callback), this); g_object_unref(file); } GAsyncReadyCallback PermissionsPropertiesPage::async_query_permisson_callback(GObject *obj, GAsyncResult *res, PermissionsPropertiesPage *p_this) { GError *err = nullptr; auto info = g_file_query_info_finish(G_FILE(obj), res, &err); if (!info) { if (p_this) { p_this->m_table->setVisible(false); p_this->m_message->setText(tr("Can not get the permission info.")); p_this->m_message->setVisible(true); } } if (err) { qDebug()<message; if (p_this) { p_this->m_table->setVisible(false); p_this->m_message->setText(tr("Can not get the permission info.")); p_this->m_message->setVisible(true); } g_error_free(err); } if (info) { if (p_this) { bool enable = true; auto table = p_this->m_table; auto user = g_file_info_get_attribute_string(info, G_FILE_ATTRIBUTE_OWNER_USER); //auto owner = g_file_info_get_attribute_string(info, G_FILE_ATTRIBUTE_OWNER_USER_REAL); QString userString = user; QString groupName = g_file_info_get_attribute_string(info, G_FILE_ATTRIBUTE_OWNER_GROUP); QString userNameDisplayString = user; bool current_user_readable = g_file_info_get_attribute_boolean(info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ); bool current_user_writeable = g_file_info_get_attribute_boolean(info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE); bool current_user_executable = g_file_info_get_attribute_boolean(info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE); p_this->m_has_unix_mode = g_file_info_has_attribute(info, G_FILE_ATTRIBUTE_UNIX_MODE); guint32 mode = 0; if (p_this->m_has_unix_mode) mode = g_file_info_get_attribute_uint32(info, G_FILE_ATTRIBUTE_UNIX_MODE); auto owner_readable = mode & S_IRUSR; auto owner_writeable = mode & S_IWUSR; auto owner_executable = mode & S_IXUSR; //read p_this->m_permissions[0][0] = owner_readable; //write p_this->m_permissions[0][1] = owner_writeable; //executable p_this->m_permissions[0][2] = owner_executable; auto group_readable = mode & S_IRGRP; auto group_writeable = mode & S_IWGRP; auto group_executable = mode & S_IXGRP; p_this->m_permissions[1][0] = group_readable; p_this->m_permissions[1][1] = group_writeable; p_this->m_permissions[1][2] = group_executable; auto other_readable = mode & S_IROTH; auto other_writeable = mode & S_IWOTH; auto other_executable = mode & S_IXOTH; p_this->m_permissions[2][0] = other_readable; p_this->m_permissions[2][1] = other_writeable; p_this->m_permissions[2][2] = other_executable; qDebug()<pw_name; if (userString == username) { userNameDisplayString += tr("(Me)"); isSelf = true; } /* if (userNameDisplayString.isEmpty()) userNameDisplayString = tr("Unkwon"); if (groupName == pw->pw_gecos) isSameGroup = true; if (groupName.isEmpty()) groupName = tr("Unkwon"); */ if (!isSelf && !isSameGroup) { qDebug()<<"the uid not permit"; enable = false; } if (pw->pw_uid == 0) { QFileInfo file("/usr/sbin/security-switch"); if(file.exists() == true) { QProcess shProcess; shProcess.start("security-switch --get"); if (!shProcess.waitForStarted()) { qDebug()<<"wait get security state start timeout"; } else { if (!shProcess.waitForFinished()) { qDebug()<<"wait get security state finshed timeout"; } else { QString secState = shProcess.readAllStandardOutput(); qDebug()<<"security-switch get test "<< secState; if (secState.contains("strict")) { qDebug()<<"now it is in strict mode, so root is not super"; } else { qDebug()<<"pw uid is 0, it is super"; enable = true; } } } } else { qDebug()<<"security-switch is not support, so it is super"; enable = true; } /* if (!kysec_is_disabled() && kysec_get_3adm_status()) { qDebug()<<"now it is in strict mode, so root is not super"; } else { qDebug()<<"pw uid is 0, it is super"; enable = true; }*/ } } else { enable = false; } if (enable) { table->setRowCount(3); //更新表格选中情况 p_this->updateCheckBox(); table->setItem(0, 0, nullptr); QTableWidgetItem* itemR0C0 = new QTableWidgetItem(QIcon::fromTheme("emblem-personal"), userNameDisplayString); table->setItem(0, 0, itemR0C0); table->setItem(1, 0, nullptr); QTableWidgetItem* itemR1C0 = new QTableWidgetItem(QIcon::fromTheme("emblem-people"), groupName); table->setItem(1, 0, itemR1C0); table->setItem(2, 0, nullptr); QTableWidgetItem* itemR2C0 = new QTableWidgetItem(QIcon::fromTheme("emblem-people"), tr("Others")); table->setItem(2, 0, itemR2C0); auto itemR0C1 = new QTableWidgetItem(tr("Owner")); itemR0C1->setTextAlignment(Qt::AlignCenter); auto itemR1C1 = new QTableWidgetItem(tr("Group")); itemR1C1->setTextAlignment(Qt::AlignCenter); auto itemR2C1 = new QTableWidgetItem(tr("Other")); itemR2C1->setTextAlignment(Qt::AlignCenter); table->setItem(0, 1, itemR0C1); table->setItem(1, 1, itemR1C1); table->setItem(2, 1, itemR2C1); table->showRow(0); table->showRow(1); table->showRow(2); } else { p_this->m_message->setText(tr("You can not change the access of this file.")); p_this->m_message->show(); table->setRowCount(1); QTableWidgetItem *itemR0C0 = new QTableWidgetItem(QIcon::fromTheme("emblem-personal"), tr("Me")); table->setItem(0, 0, nullptr); table->setItem(0, 0, itemR0C0); auto itemR0C1 = new QTableWidgetItem(tr("User")); itemR0C1->setTextAlignment(Qt::AlignCenter); table->setItem(0, 1, itemR0C1); for (int i = 0; i < 3; i++) { table->setCellWidget(0, i + 2, nullptr); QWidget *w = new QWidget(table); QHBoxLayout *l = new QHBoxLayout(w); l->setMargin(0); w->setLayout(l); l->setAlignment(Qt::AlignCenter); auto checkbox = new QCheckBox(w); l->addWidget(checkbox); table->setCellWidget(0, i + 2, w); switch (i) { case 0: checkbox->setChecked(current_user_readable); break; case 1: checkbox->setChecked(current_user_writeable); break; case 2: checkbox->setChecked(current_user_executable); break; } } } table->setEnabled(enable); //防止误修改 p_this->m_enable = enable; } g_object_unref(info); } return nullptr; } void PermissionsPropertiesPage::changePermission(int row, int column, bool checked) { if(!m_enable) return; m_permissions[row][column] = checked; this->thisPageChanged(); this->updateCheckBox(); } /*! * update file ermissions * \brief PermissionsPropertiesPage::savePermissions */ void PermissionsPropertiesPage::savePermissions() { /*! \bug even though directory know the file's attributes have been changed, and model request updated the data, the view doesn't paint the current emblems correctly. */ //FIXME: should use g_file_set_attribute() with mode info? if(!m_enable) return; mode_t mod = 0; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { bool b = m_permissions[i][j]; if (b) { int rc = i*10 + j; switch (rc) { case 0: { mod |= S_IRUSR; break; } case 1: { mod |= S_IWUSR; break; } case 2: { mod |= S_IXUSR; break; } case 10: { mod |= S_IRGRP; break; } case 11: { mod |= S_IWGRP; break; } case 12: { mod |= S_IXGRP; break; } case 20: { mod |= S_IROTH; break; } case 21: { mod |= S_IWOTH; break; } case 22: { mod |= S_IXOTH; break; } } } } } if (m_has_unix_mode) { g_autoptr(GFile) pfile = g_file_new_for_uri(m_uri.toUtf8().constData()); g_file_set_attribute_uint32(pfile, G_FILE_ATTRIBUTE_UNIX_MODE, (guint32)mod, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, nullptr, nullptr); } } void PermissionsPropertiesPage::saveAllChange() { if(this->m_thisPageChanged) this->savePermissions(); qDebug() << "PermissionsPropertiesPage::saveAllChange()" << this->m_thisPageChanged; } void PermissionsPropertiesPage::thisPageChanged() { this->m_thisPageChanged = true; } void PermissionsPropertiesPage::updateCheckBox() { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { m_table->setCellWidget(i, j + 2, nullptr); QWidget *w = new QWidget(m_table); QHBoxLayout *l = new QHBoxLayout(w); l->setMargin(0); w->setLayout(l); l->setAlignment(Qt::AlignCenter); auto checkbox = new QCheckBox(w); l->addWidget(checkbox); m_table->setCellWidget(i, j + 2, w); checkbox->setChecked(this->m_permissions[i][j]); //disable home path bool check_enable = true; QString uri = m_uri; if(uri.startsWith("filesafe:///")){ QStringList list = uri.split("/"); if(list.size()==4){ check_enable = false; } } QString homeUri = "file://" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation); if (this->m_uri == homeUri || !check_enable) checkbox->setDisabled(true); else checkbox->setDisabled(false); connect(checkbox, &QCheckBox::clicked, this, [=]() { qDebug()<<"clicked"<isChecked(); this->checkBoxChanged(i, j, checkbox->isChecked()); }); } } } QWidget *PermissionsPropertiesPage::createCellWidget(QWidget *parent, QIcon icon, QString text) { QWidget *widget = new QWidget(parent); QHBoxLayout *layout = new QHBoxLayout(widget); layout->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); layout->setContentsMargins(22, 0, 0, 0); //组件间距 - Widget spacing layout->setSpacing(9); QPushButton *cellIcon = new QPushButton(widget); cellIcon->setStyleSheet("QPushButton{" "border-radius: 8px; " "background-color: transparent;" "max-width:16px;" "max-height:16px;" "min-width:16px;" "min-height:16px;" "}"); cellIcon->setEnabled(false); cellIcon->setIcon(icon); cellIcon->setIconSize(QSize(16, 16)); layout->addWidget(cellIcon); QLabel *label = new QLabel(widget); label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); QFontMetrics fontMetrics = label->fontMetrics(); int fontSize = fontMetrics.width(text); QString str = text; //widget宽度200px;设计稿在左边空出22px;icon宽度16px,icon右侧9px; //200-22-16-9 = 153 , widget:剩下的3px给右侧留空 -_-; if(fontSize > 150) { widget->setToolTip(text); str = fontMetrics.elidedText(text, Qt::ElideRight, 150); } label->setText(str); layout->addWidget(label); return widget; } peony/libpeony-qt/controls/property-page/details-properties-page.h0000644000175000017500000000522614205101223024431 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2021, 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 . * * Authors: Wenfei He * */ #ifndef DETAILSPROPERTIESPAGE_H #define DETAILSPROPERTIESPAGE_H #include "properties-window-tab-iface.h" #include "file-info-job.h" #include #include #include #include namespace Peony { class FileWatcher; class DetailsPropertiesPage : public PropertiesWindowTabIface { Q_OBJECT public: explicit DetailsPropertiesPage(const QString &uri, QWidget *parent = nullptr); ~DetailsPropertiesPage(); /*! * \brief 统一创建新的label * \param minWidth * \param minHeight * \param text * \param parent * \return */ QLabel *createFixedLabel(quint64 minWidth, quint64 minHeight, QString text, QWidget *parent); void initDetailsPropertiesPage(); void saveAllChange() override; public: // void getFIleInfo(); Q_SIGNALS: void fileInfoReady(); private: QString m_uri = nullptr; std::shared_ptr m_fileInfo = nullptr; std::shared_ptr m_watcher; QString m_systemTimeFormat = nullptr; QVBoxLayout *m_layout = nullptr; QTableWidget *m_tableWidget = nullptr; QLabel *m_createDateLabel = nullptr; QLabel *m_modifyDateLabel = nullptr; //image file QLabel *m_imageWidthLabel = nullptr; QLabel *m_imageHeightLabel = nullptr; QLabel *m_imageDepthLabel = nullptr; QLabel *m_ownerLabel = nullptr; QLabel *m_computerLabel = nullptr; QLabel *m_localLabel = nullptr; QLabel *m_nameLabel = nullptr; QWidget *createTableRow(QString labelText, QString content); QWidget *createTableRow(QString labelText, QLabel *contentLabel); void addRow(QString labelText, QString content); void addRow(QString labelText, QLabel *contentLabel); void initTableWidget(); void setSystemTimeFormat(QString format); void updateFileInfo(const QString &uri); }; } #endif //DETAILSPROPERTIESPAGE_H peony/libpeony-qt/controls/property-page/computer-properties-page-factory.cpp0000644000175000017500000000313514205101223026637 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "computer-properties-page-factory.h" #include "computer-properties-page.h" using namespace Peony; static ComputerPropertiesPageFactory *global_instance = nullptr; ComputerPropertiesPageFactory::ComputerPropertiesPageFactory(QObject *parent) : QObject(parent) { } ComputerPropertiesPageFactory *ComputerPropertiesPageFactory::getInstance() { if (!global_instance) global_instance = new ComputerPropertiesPageFactory; return global_instance; } bool ComputerPropertiesPageFactory::supportUris(const QStringList &uris) { if (uris.count() != 1) return false; if (!uris.first().contains("computer:///")) return false; return true; } PropertiesWindowTabIface *ComputerPropertiesPageFactory::createTabPage(const QStringList &uris) { return new ComputerPropertiesPage(uris.first()); } peony/libpeony-qt/controls/property-page/open-with-properties-page.h0000644000175000017500000001070614205115226024725 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Wenfei He * */ #ifndef OPENWITHPROPERTIESPAGE_H #define OPENWITHPROPERTIESPAGE_H #include "properties-window-tab-iface.h" #include "file-label-model.h" #include "file-info-job.h" #include "file-launch-action.h" #include #include #include #include #include #include #include #include namespace Peony { class LaunchHashList { public: static LaunchHashList *getAllLaunchHashList(const QString &uri, QWidget *parent = nullptr); LaunchHashList(); LaunchHashList(const QString &uri, QWidget *parent = nullptr); ~LaunchHashList(); public: //保存该文件的全部打开方式 - Save all open methods of the file QListWidget *m_actionList = nullptr; //每个listItem对应的launchAction - Launch Action corresponding to each list Item QHash *m_actionHash = nullptr; }; class NewFileLaunchDialog : public QDialog { Q_OBJECT public: explicit NewFileLaunchDialog(const QString &uri, QWidget *parent = nullptr); virtual ~NewFileLaunchDialog(); QSize sizeHint() const override { return QSize(400, 600); } private: QVBoxLayout *m_layout = nullptr; QDialogButtonBox *m_button_box = nullptr; LaunchHashList *m_launchHashList = nullptr; }; class AllFileLaunchDialog : public QDialog { Q_OBJECT public: explicit AllFileLaunchDialog(const QString &uri, QWidget *parent = nullptr); virtual ~AllFileLaunchDialog(); QSize sizeHint() const override { return QSize(400, 600); } private: QVBoxLayout *m_layout = nullptr; QDialogButtonBox *m_button_box = nullptr; LaunchHashList *m_launchHashList = nullptr; }; //默认打开方式组件 class DefaultOpenWithWidget : public QWidget { Q_OBJECT private: FileLaunchAction* m_launchAction = nullptr; QLabel* m_appNameLabel = nullptr; QLabel* m_appIconLabel = nullptr; QHBoxLayout* m_layout = nullptr; public: explicit DefaultOpenWithWidget(QWidget *parent = nullptr); ~DefaultOpenWithWidget() override; /** * \brief 设置应用图标 * \param appIcon */ void setAppIcon(QIcon appIcon); /** * \brief 设置应用名称 * \param appName */ void setAppName(QString appName); /*! * \brief 获取当前打开方式 */ FileLaunchAction* getLaunchAction(); void setLaunchAction(FileLaunchAction* launchAction); protected: void resizeEvent(QResizeEvent *event) override; }; //open with page class OpenWithPropertiesPage : public PropertiesWindowTabIface { Q_OBJECT public: /*! * \brief 创建默认打开方式的Widget * \param uri * \param parent * \return */ static DefaultOpenWithWidget* createDefaultOpenWithWidget(const QString &uri, QWidget *parent = nullptr); public: void initFloorOne(); void initFloorTwo(); void initFloorThree(); void addSeparator() { QFrame *separator = new QFrame(this); separator->setFrameShape(QFrame::HLine); m_layout->addWidget(separator); } public: explicit OpenWithPropertiesPage(const QString &uri, QWidget *parent = nullptr); ~OpenWithPropertiesPage(); void init(); void saveAllChange() override; private: QVBoxLayout *m_layout = nullptr; std::shared_ptr m_fileInfo = nullptr; QFutureWatcher *m_futureWatcher = nullptr; //新的打开方式 FileLaunchAction *m_newAction = nullptr; LaunchHashList *m_launchHashList = nullptr; //默认打开方式 DefaultOpenWithWidget* m_defaultOpenWithWidget = nullptr; }; } #endif //OPENWITHPROPERTIESPAGE_H peony/libpeony-qt/controls/property-page/permissions-properties-page-factory.h0000644000175000017500000000421114205101223027015 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef PERMISSIONSPROPERTIESPAGEFACTORY_H #define PERMISSIONSPROPERTIESPAGEFACTORY_H #include #include "peony-core_global.h" #include "properties-window-tab-page-plugin-iface.h" namespace Peony { class PEONYCORESHARED_EXPORT PermissionsPropertiesPageFactory : public QObject, public PropertiesWindowTabPagePluginIface { Q_OBJECT public: static PermissionsPropertiesPageFactory *getInstance(); //plugin iface const QString name() override { return QObject::tr("Permissions"); } PluginType pluginType() override { return PluginType::PropertiesWindowPlugin; } const QString description() override { return QObject::tr("Show and modify file's permission, owner and group."); } const QIcon icon() override { return QIcon::fromTheme("view-paged-symbolic", QIcon::fromTheme("folder")); } void setEnable(bool enable) override { Q_UNUSED(enable) } bool isEnable() override { return true; } //properties plugin iface int tabOrder() override { return 800; } bool supportUris(const QStringList &uris) override; PropertiesWindowTabIface *createTabPage(const QStringList &uris) override; void closeFactory() override; private: explicit PermissionsPropertiesPageFactory(QObject *parent = nullptr); }; } #endif // PERMISSIONSPROPERTIESPAGEFACTORY_H peony/libpeony-qt/controls/property-page/computer-properties-page-factory.h0000644000175000017500000000426314205101223026307 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef COMPUTERPROPERTIESPAGEFACTORY_H #define COMPUTERPROPERTIESPAGEFACTORY_H #include #include "peony-core_global.h" #include "properties-window-tab-page-plugin-iface.h" namespace Peony { class PEONYCORESHARED_EXPORT ComputerPropertiesPageFactory : public QObject, public PropertiesWindowTabPagePluginIface { Q_OBJECT public: static ComputerPropertiesPageFactory *getInstance(); //plugin iface const QString name() override { return QObject::tr("Computer Properties"); } PluginType pluginType() override { return PluginType::PropertiesWindowPlugin; } const QString description() override { return QObject::tr("Show the computer properties or items in computer."); } const QIcon icon() override { return QIcon::fromTheme("view-paged-symbolic", QIcon::fromTheme("folder")); } void setEnable(bool enable) override { Q_UNUSED(enable) } bool isEnable() override { return true; } //properties plugin iface int tabOrder() override { return 1000-1; } bool supportUris(const QStringList &uris) override; PropertiesWindowTabIface *createTabPage(const QStringList &uris) override; void closeFactory() override {} private: explicit ComputerPropertiesPageFactory(QObject *parent = nullptr); ~ComputerPropertiesPageFactory() override {} }; } #endif // COMPUTERPROPERTIESPAGEFACTORY_H peony/libpeony-qt/controls/property-page/open-with-properties-page-factory.h0000644000175000017500000000413514205101223026361 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Wenfei He * */ #ifndef OPENWITHPROPERTIESPAGEFACTORY_H #define OPENWITHPROPERTIESPAGEFACTORY_H #include #include "peony-core_global.h" #include "properties-window-tab-page-plugin-iface.h" namespace Peony { class OpenWithPropertiesPageFactory : public QObject, public PropertiesWindowTabPagePluginIface { Q_OBJECT public: static OpenWithPropertiesPageFactory *getInstance(); //plugin iface const QString name() override { return QObject::tr("Open With"); } PluginType pluginType() override { return PluginType::PropertiesWindowPlugin; } const QString description() override { return QObject::tr("open with."); } const QIcon icon() override { return QIcon::fromTheme("view-paged-symbolic", QIcon::fromTheme("folder")); } void setEnable(bool enable) override { Q_UNUSED(enable) } bool isEnable() override { return true; } //properties plugin iface int tabOrder() override { return 700; } bool supportUris(const QStringList &uris) override; PropertiesWindowTabIface *createTabPage(const QStringList &uris) override; void closeFactory() override; private: explicit OpenWithPropertiesPageFactory(QObject *parent = nullptr); ~OpenWithPropertiesPageFactory(); }; } #endif //OPENWITHPROPERTIESPAGEFACTORY_H peony/libpeony-qt/controls/property-page/recent-and-trash-properties-page-factory.h0000644000175000017500000000437614205101223027615 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef RECENTANDTRASHPROPERTIESPAGEFACTORY_H #define RECENTANDTRASHPROPERTIESPAGEFACTORY_H #include #include "peony-core_global.h" #include "properties-window-tab-page-plugin-iface.h" namespace Peony { class PEONYCORESHARED_EXPORT RecentAndTrashPropertiesPageFactory : public QObject, public PropertiesWindowTabPagePluginIface { Q_OBJECT public: static RecentAndTrashPropertiesPageFactory *getInstance(); //plugin iface const QString name() override { return QObject::tr("Trash and Recent"); } PluginType pluginType() override { return PluginType::PropertiesWindowPlugin; } const QString description() override { return QObject::tr("Show the file properties or items in trash or recent."); } const QIcon icon() override { return QIcon::fromTheme("view-paged-symbolic", QIcon::fromTheme("folder")); } void setEnable(bool enable) override { Q_UNUSED(enable) } bool isEnable() override { return true; } //properties plugin iface int tabOrder() override { return 1000-3; } bool supportUris(const QStringList &uris) override; PropertiesWindowTabIface *createTabPage(const QStringList &uris) override; void closeFactory() override { this->deleteLater(); } private: explicit RecentAndTrashPropertiesPageFactory(QObject *parent = nullptr); ~RecentAndTrashPropertiesPageFactory() override {} }; } #endif // RECENTANDTRASHPROPERTIESPAGEFACTORY_H peony/libpeony-qt/controls/property-page/recent-and-trash-properties-page-factory.cpp0000644000175000017500000000332114205101223030135 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "recent-and-trash-properties-page-factory.h" #include "recent-and-trash-properties-page.h" #include using namespace Peony; static RecentAndTrashPropertiesPageFactory *global_instance = nullptr; RecentAndTrashPropertiesPageFactory *RecentAndTrashPropertiesPageFactory::getInstance() { if (!global_instance) global_instance = new RecentAndTrashPropertiesPageFactory; return global_instance; } RecentAndTrashPropertiesPageFactory::RecentAndTrashPropertiesPageFactory(QObject *parent) : QObject(parent) { } bool RecentAndTrashPropertiesPageFactory::supportUris(const QStringList &uris) { if (uris.count() != 1) return false; if (uris.first().contains("trash:///") || uris.first().contains("recent:///")) return true; return false; } PropertiesWindowTabIface *RecentAndTrashPropertiesPageFactory::createTabPage(const QStringList &uris) { return new RecentAndTrashPropertiesPage(uris); } peony/libpeony-qt/controls/property-page/recent-and-trash-properties-page.h0000644000175000017500000000312514205101223026137 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef RECENTANDTRASHPROPERTIESPAGE_H #define RECENTANDTRASHPROPERTIESPAGE_H #include #include #include "file-info.h" #include "properties-window-tab-iface.h" #include "peony-core_global.h" class QFormLayout; namespace Peony { class PEONYCORESHARED_EXPORT RecentAndTrashPropertiesPage : public PropertiesWindowTabIface { Q_OBJECT public: explicit RecentAndTrashPropertiesPage(const QStringList &uris, QWidget *parent = nullptr); protected: void addSeparator(); void init(); QString getIconName(); private: QString m_uri; QFormLayout *m_layout; std::shared_ptr m_fileInfo = nullptr; QFutureWatcher *m_futureWatcher = nullptr; // PropertiesWindowTabIface interface public: void saveAllChange(); }; } #endif // RECENTANDTRASHPROPERTIESPAGE_H peony/libpeony-qt/controls/menu/0000755000175000017500000000000014205115226015700 5ustar fengfengpeony/libpeony-qt/controls/menu/menu-plugin-manager.cpp0000644000175000017500000001705014205101223022247 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "menu-plugin-manager.h" #include "file-info.h" //create link #include #include #include #include #include #include #include //create link //tag file #include "file-label-model.h" #include #include #include #include using namespace Peony; static MenuPluginManager *global_instance = nullptr; MenuPluginManager::MenuPluginManager(QObject *parent) : QObject(parent) { registerPlugin(new CreateLinkInternalPlugin(this)); registerPlugin(new FileLabelInternalMenuPlugin(this)); } MenuPluginManager::~MenuPluginManager() { } bool MenuPluginManager::registerPlugin(MenuPluginInterface *plugin) { if (m_hash.value(plugin->name())) { return false; } m_hash.insert(plugin->name(), plugin); return true; } MenuPluginManager *MenuPluginManager::getInstance() { if (!global_instance) { global_instance = new MenuPluginManager; } return global_instance; } void MenuPluginManager::close() { this->deleteLater(); } const QStringList MenuPluginManager::getPluginIds() { return m_hash.keys(); } MenuPluginInterface *MenuPluginManager::getPlugin(const QString &pluginId) { return m_hash.value(pluginId); } //CreateLinkInternalPlugin CreateLinkInternalPlugin::CreateLinkInternalPlugin(QObject *parent) : QObject (parent) { } QList CreateLinkInternalPlugin::menuActions(MenuPluginInterface::Types types, const QString &uri, const QStringList &selectionUris) { QList l; if (types == MenuPluginInterface::DesktopWindow || types == MenuPluginInterface::DirectoryView) { if (selectionUris.count() == 1) { auto select_file_info = FileInfo::fromUri(selectionUris[0]); if(select_file_info->isSymbolLink()) return l; QString str_cmp = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); str_cmp.insert(0, QString("file://")); //在桌面文件夹中屏蔽 “发送到桌面快捷方式” 和 “创建链接到...” - Block "Create link to desktop" and "Create link to..." in the desktop folder if(QString::compare(QUrl::fromPercentEncoding(uri.toLocal8Bit()), str_cmp)) { auto createLinkToDesktop = new QAction(QIcon::fromTheme("emblem-link-symbolic"), tr("Create Link to Desktop"), nullptr); auto info = FileInfo::fromUri(selectionUris.first()); QString desktopPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); QString originPath = QUrl(selectionUris.first()).path(); //special type mountable, or isVirtual then return if (selectionUris.first().startsWith("computer:///") || info->isVirtual() || selectionUris.first().startsWith("trash:///") || selectionUris.first().startsWith("recent:///") || selectionUris.first().startsWith("mtp://") || originPath == desktopPath) { return l; } connect(createLinkToDesktop, &QAction::triggered, [=]() { //QUrl src = selectionUris.first(); QString desktopUri = "file://" + desktopPath; FileLinkOperation *op = new FileLinkOperation(selectionUris.first(), desktopUri); op->setAutoDelete(true); FileOperationManager::getInstance()->startOperation(op, true); }); l<setAutoDelete(true); FileOperationManager::getInstance()->startOperation(op, true); } }); l< FileLabelInternalMenuPlugin::menuActions(MenuPluginInterface::Types types, const QString &uri, const QStringList &selectionUris) { QList l; //fix virtual path add label fail issue auto info = FileInfo::fromUri(uri); if (info->isVirtual()) return l; if (types == DirectoryView) { if (selectionUris.count() == 1) { //not allow in trash path if (uri.startsWith("trash://") || uri.startsWith("smb://") || uri.startsWith("recent://") || uri.startsWith("computer://")) return l; auto action = new QAction(tr("Add File Label..."), nullptr); auto uri = selectionUris.first(); auto menu = new QMenu(); auto items = FileLabelModel::getGlobalModel()->getAllFileLabelItems(); for (auto item : items) { auto ids = FileLabelModel::getGlobalModel()->getFileLabelIds(uri); bool checked = ids.contains(item->id()); auto a = menu->addAction(item->name(), [=]() { if (!checked) { // note: while add label to file at first time (usually new user created), // it might fail to add a label correctly, but second time will work. // it might be a bug of gvfsd-metadata. anyway we should to avoid this // situation. FileLabelModel::getGlobalModel()->addLabelToFile(uri, item->id()); FileLabelModel::getGlobalModel()->addLabelToFile(uri, item->id()); } else { FileLabelModel::getGlobalModel()->removeFileLabel(uri, item->id()); } }); a->setCheckable(true); a->setChecked(checked); } menu->addSeparator(); menu->addAction(tr("Delete All Label"), [=]() { FileLabelModel::getGlobalModel()->removeFileLabel(uri); }); action->setMenu(menu); l<. * * Authors: Yue Lan * */ #ifndef MENUPLUGINMANAGER_H #define MENUPLUGINMANAGER_H #include #include "peony-core_global.h" #include "menu-plugin-iface.h" namespace Peony { class MenuPluginManager : public QObject { Q_OBJECT public: bool registerPlugin(MenuPluginInterface *plugin); static MenuPluginManager *getInstance(); const QStringList getPluginIds(); MenuPluginInterface *getPlugin(const QString &pluginId); void close(); private: QHash m_hash; explicit MenuPluginManager(QObject *parent = nullptr); ~MenuPluginManager(); }; class CreateLinkInternalPlugin : public QObject, public MenuPluginInterface { Q_OBJECT public: explicit CreateLinkInternalPlugin(QObject *parent); PluginInterface::PluginType pluginType() override { return PluginInterface::MenuPlugin; } const QString name() override { return tr("Peony-Qt Create Link Extension"); } const QString description() override { return tr("Create Link Menu Extension."); } const QIcon icon() override { return QIcon::fromTheme("emblem-symbolic-link"); } void setEnable(bool enable) override { m_enable = enable; } bool isEnable() override { return m_enable; } QString testPlugin() override { return "test create link"; } QList menuActions(Types types, const QString &uri, const QStringList &selectionUris) override; private: bool m_enable = true; }; class FileLabelInternalMenuPlugin : public QObject, public MenuPluginInterface { Q_OBJECT public: explicit FileLabelInternalMenuPlugin(QObject *parent); PluginInterface::PluginType pluginType() override { return PluginInterface::MenuPlugin; } const QString name() override { return tr("Peony File Labels Menu Extension"); } const QString description() override { return tr("Tag a File with Menu."); } const QIcon icon() override { return QIcon::fromTheme("emblem-symbolic-link"); } void setEnable(bool enable) override { m_enable = enable; } bool isEnable() override { return m_enable; } QString testPlugin() override { return "test create file label"; } QList menuActions(Types types, const QString &uri, const QStringList &selectionUris) override; private: bool m_enable = true; }; } #endif // MENUPLUGINMANAGER_H peony/libpeony-qt/controls/menu/directory-view-menu/0000755000175000017500000000000014205115226021616 5ustar fengfengpeony/libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.h0000644000175000017500000000543514205115226025714 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef DIRECTORYVIEWMENU_H #define DIRECTORYVIEWMENU_H #include #include "peony-core_global.h" #include "directory-view-plugin-iface2.h" namespace Peony { class FMWindowIface; /*! * \brief The DirectoryViewMenu class * * \todo * add create template function. */ class PEONYCORESHARED_EXPORT DirectoryViewMenu : public QMenu { Q_OBJECT public: /*! * \brief DirectoryViewMenu * \param directoryView * \param parent * \deprecated */ explicit DirectoryViewMenu(DirectoryViewWidget *directoryView, QWidget *parent = nullptr); explicit DirectoryViewMenu(FMWindowIface *window, QWidget *parent = nullptr); const QStringList &urisToEdit() { return m_uris_to_edit; } protected: void fillActions(); const QList constructOpenOpActions(); const QList constructCreateTemplateActions(); const QList constructViewOpActions(); const QList constructFileOpActions(); const QList constructMenuPluginActions(); //directory view menu extension. const QList constructFilePropertiesActions(); const QList constructComputerActions(); const QList constructTrashActions(); const QList constructSearchActions(); private: FMWindowIface *m_top_window; DirectoryViewWidget *m_view; QString m_directory; QStringList m_selections; bool m_is_cd = false; bool m_is_ftp = false; bool m_is_computer = false; bool m_is_network = false; bool m_is_trash = false; bool m_is_search = false; bool m_is_recent = false; bool m_is_favorite = false; bool m_can_delete = true; // if contains computer:/// trash:/// recent:/// and desktop path bool m_is_kydroid = false; bool m_is_filesafe = false; bool m_is_filebox_file = false; //filesafe path files operation flag bool m_is_smb_file = false; const int ELIDE_TEXT_LENGTH = 16; QStringList m_uris_to_edit; }; } #endif // DIRECTORYVIEWMENU_H peony/libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.cpp0000644000175000017500000012541414205115226026247 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "directory-view-menu.h" #include "directory-view-plugin-iface2.h" #include "directory-view-widget.h" #include "fm-window.h" #include "directory-view-container.h" #include "menu-plugin-manager.h" #include "file-info-job.h" #include "file-info.h" #include "directory-view-factory-manager.h" #include "view-factory-model.h" #include "view-factory-sort-filter-model.h" #include "clipboard-utils.h" #include "file-operation-utils.h" #include "file-operation-manager.h" //FileOpInfo #include "audio-play-manager.h" #include "file-utils.h" #include "bookmark-manager.h" #include "volume-manager.h" #include "properties-window.h" #include "windows/format_dialog.h" #include "file-launch-manager.h" #include "file-launch-action.h" #include "file-lauch-dialog.h" #include "file-operation-error-dialog.h" #include "file-enumerator.h" #include "gerror-wrapper.h" #include "global-settings.h" #include #include #include #include #include #include #include #include #include #include using namespace Peony; DirectoryViewMenu::DirectoryViewMenu(DirectoryViewWidget *directoryView, QWidget *parent) : QMenu(parent) { m_top_window = nullptr; m_view = directoryView; m_directory = directoryView->getDirectoryUri(); m_selections = directoryView->getSelections(); fillActions(); } DirectoryViewMenu::DirectoryViewMenu(FMWindowIface *window, QWidget *parent) : QMenu(parent) { m_top_window = window; m_view = window->getCurrentPage()->getView(); //setParent(dynamic_cast(m_view)); m_directory = window->getCurrentUri(); m_selections = window->getCurrentSelections(); fillActions(); } void DirectoryViewMenu::fillActions() { if (m_directory == "computer:///") { m_is_computer = true; } if (m_directory == "network:///") { m_is_network = true; } if (m_directory == "trash:///") { m_is_trash = true; } if (m_directory.startsWith("search://")) { m_is_search = true; } if (m_directory.startsWith("burn://")) { m_is_cd = true; } if (m_directory.startsWith("recent://")) { m_is_recent = true; } if (m_directory.startsWith("favorite://")) { m_is_favorite = true; } if (m_directory.startsWith("kydroid:///") || m_directory.startsWith("kmre:///")) { m_is_kydroid = true; } if (m_directory.startsWith("ftp://") || m_directory.startsWith("sftp://")) { m_is_ftp = true; } if (m_directory.startsWith("filesafe:///")){ m_is_filebox_file = true; } if(m_directory == "filesafe:///" || m_directory.startsWith("search:///search_uris=filesafe:///&")) { m_is_filesafe = true; } if(m_directory.startsWith("smb://")){ m_is_smb_file = true; } QString homeUri = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); QString musicUri = QStandardPaths::writableLocation(QStandardPaths::MusicLocation); QString desktop = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); QString videoUri = QStandardPaths::writableLocation(QStandardPaths::MoviesLocation); QString docUri = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); QString pictureUri = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); QString downloadUri = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); for (auto uriIndex = 0; uriIndex < m_selections.count(); ++uriIndex) { qDebug() << desktop; if (m_selections.at(uriIndex) == "favorite:///?schema=trash" || m_selections.at(uriIndex) == "favorite:///?schema=kmre" || m_selections.at(uriIndex) == "favorite:///?schema=recent" || m_selections.at(uriIndex) == "favorite:///data/usershare?schema=file" || m_selections.at(uriIndex) == "favorite://" + homeUri + "?schema=file" || m_selections.at(uriIndex) == "favorite://" + musicUri + "?schema=file" || m_selections.at(uriIndex) == "favorite://" + desktop + "?schema=file" || m_selections.at(uriIndex) == "favorite://" + videoUri + "?schema=file" || m_selections.at(uriIndex) == "favorite://" + docUri + "?schema=file" || m_selections.at(uriIndex) == "favorite://" + pictureUri + "?schema=file" || m_selections.at(uriIndex) == "favorite://" + downloadUri + "?schema=file") { m_can_delete = false; break; } } //add open actions auto openActions = constructOpenOpActions(); if (!openActions.isEmpty()) addSeparator(); //netwotk items not show operation menu if (m_is_network) return; if (! m_is_kydroid){ //create template actions auto templateActions = constructCreateTemplateActions(); if (!templateActions.isEmpty()) addSeparator(); //add view actions auto viewActions = constructViewOpActions(); if (!viewActions.isEmpty()) addSeparator(); } //add operation actions auto fileOpActions = constructFileOpActions(); if (!fileOpActions.isEmpty()) addSeparator(); if (! m_is_kydroid){ //add plugin actions auto pluginActions = constructMenuPluginActions(); if (!pluginActions.isEmpty()) addSeparator(); } //add propertries actions auto propertiesAction = constructFilePropertiesActions(); if (!propertiesAction.isEmpty()) addSeparator(); if (! m_is_kydroid){ //add actions in computer:/// auto computerActions = constructComputerActions(); if (!computerActions.isEmpty()) addSeparator(); //add actions in trash:/// auto trashActions = constructTrashActions(); if (!trashActions.isEmpty()) addSeparator(); //add actions in search:/// auto searchActions = constructSearchActions(); } } const QList DirectoryViewMenu::constructOpenOpActions() { QList l; if (m_is_trash) return l; bool isBackgroundMenu = m_selections.isEmpty(); if (isBackgroundMenu) { l<create(m_directory); auto newWindow = dynamic_cast(windowIface); newWindow->setAttribute(Qt::WA_DeleteOnClose); //FIXME: show when prepared? newWindow->show(); }); l<addNewTabs(uris); }); } else { if (m_selections.count() == 1) { auto info = FileInfo::fromUri(m_selections.first()); auto displayName = info->displayName(); if (displayName.isEmpty()) displayName = FileUtils::getFileDisplayName(info->uri()); //when name is too long, show elideText //qDebug() << "charWidth:" < ELIDE_TEXT_LENGTH) { int charWidth = fontMetrics().averageCharWidth(); displayName = fontMetrics().elidedText(displayName, Qt::ElideRight, ELIDE_TEXT_LENGTH * charWidth); } if (info->isDir()) { //add to bookmark option if (!info->isVirtual() && !info->uri().startsWith("smb://") && !m_is_kydroid && !m_is_filesafe && !m_is_filebox_file) { l<uri(); auto bookmark = BookMarkManager::getInstance(); if (bookmark->isLoaded()) { bookmark->addBookMark(info->uri()); } }); } l<goToUri(m_selections.first(), true); }); auto recommendActions = FileLaunchManager::getRecommendActions(m_selections.first()); if (recommendActions.count() >1) { auto openWithAction = addAction(tr("Open with...")); QMenu *openWithMenu = new QMenu(this); // do not highlight application icons. openWithMenu->setProperty("skipHighlightIconEffect", true); for (auto action : recommendActions) { action->setParent(openWithMenu); openWithMenu->addAction(static_cast(action)); } auto fallbackActions = FileLaunchManager::getFallbackActions(m_selections.first()); for (auto action : fallbackActions) { action->setParent(openWithMenu); openWithMenu->addAction(static_cast(action)); } openWithMenu->addSeparator(); openWithMenu->addAction(tr("More applications..."), [=]() { FileLauchDialog d(m_selections.first()); d.exec(); }); openWithAction->setMenu(openWithMenu); } l<create(m_selections.first()); auto newWindow = dynamic_cast(windowIface); newWindow->setAttribute(Qt::WA_DeleteOnClose); //FIXME: show when prepared? newWindow->show(); }); l<addNewTabs(m_selections); }); } else if (!info->isVolume()) { l<goToUri(uri, true); else FileLaunchManager::openAsync(uri, false, false); }); if (m_is_network) return l; auto openWithAction = addAction(tr("Open with...")); QMenu *openWithMenu = new QMenu(this); // do not highlight application icons. openWithMenu->setProperty("skipHighlightIconEffect", true); //auto targetUri = FileUtils::getTargetUri(m_selections.first()); //use origin uri instead of target uri, fix recommand menu not same with desktop issue //link to bug#80207 auto recommendActions = FileLaunchManager::getRecommendActions(m_selections.first()); auto fallbackActions = FileLaunchManager::getFallbackActions(m_selections.first()); //fix has default open app but no recommend actions issue, link to bug#61365 //fix open options has two same app issue, linkto bug#74480 if (recommendActions.count() == 0 && fallbackActions.count() == 0) { auto action = FileLaunchManager::getDefaultAction(m_selections.first()); if (action != NULL && action->getAppInfoDisplayName().length() > 0) recommendActions.append(action); } for (auto action : recommendActions) { action->setParent(openWithMenu); openWithMenu->addAction(static_cast(action)); } for (auto action : fallbackActions) { action->setParent(openWithMenu); openWithMenu->addAction(static_cast(action)); } openWithMenu->addSeparator(); openWithMenu->addAction(tr("More applications..."), [=]() { FileLauchDialog d(m_selections.first()); d.exec(); }); openWithAction->setMenu(openWithMenu); } else { l<goToUri(uri, true); }); } } else { l< fileMap; /**step 1: Categorize files according to type. * step 2: Open files in batches to avoid loss of asynchronous messages due to program startup. **/ for (auto uri : m_selections) { auto info = FileInfo::fromUri(uri); if (info->isDir() || info->isVolume()) { dirs<getAppInfoName(); QStringList list; if (fileMap.contains(defaultAppName)) { list = fileMap[defaultAppName]; list << uri; fileMap.insert(defaultAppName, list); } else { list << uri; fileMap.insert(defaultAppName, list); } } } if (!dirs.isEmpty()) m_top_window->addNewTabs(dirs); if(!fileMap.empty()) { QMap::iterator iter = fileMap.begin(); while (iter != fileMap.end()) { FileLaunchManager::openAsync(iter.value()); iter++; } } }); } } return l; } const QList DirectoryViewMenu::constructCreateTemplateActions() { QList l; if (!m_is_favorite && m_selections.isEmpty() && !m_is_filesafe && !m_is_trash) { auto createAction = new QAction(tr("New..."), this); if (m_is_cd) { createAction->setEnabled(false); } //fix create folder fail issue in special path auto info = FileInfo::fromUri(m_directory); if (info.get()->isEmptyInfo()) { //FIXME: replace BLOCKING api in ui thread. FileInfoJob job(info); job.querySync(); } if (!info->canWrite() && !m_is_ftp) { createAction->setEnabled(false); } if(m_top_window->getFilterWorking()) { createAction->setEnabled(false); } l<setMenu(subMenu); addAction(createAction); //enumerate template dir // QDir templateDir(g_get_user_special_dir(G_USER_DIRECTORY_TEMPLATES)); QString templatePath = GlobalSettings::getInstance()->getValue(TEMPLATES_DIR).toString(); qWarning()<<"tempalte Path is"< info = FileInfo::fromUri(uri_str); QString mimeType = info->mimeType(); if (mimeType.isEmpty()) { FileInfoJob job(info); job.querySync(); mimeType = info->mimeType(); } QIcon tmpIcon; GList *app_infos = g_app_info_get_recommended_for_type(mimeType.toUtf8().constData()); GList *l = app_infos; QList actions; bool isOnlyUnref = false; while (l) { auto app_info = static_cast(l->data); if (!isOnlyUnref) { GThemedIcon *icon = G_THEMED_ICON(g_app_info_get_icon(app_info)); const char * const * icon_names = g_themed_icon_get_names(icon); if (icon_names) tmpIcon = QIcon::fromTheme(*icon_names); if(!tmpIcon.isNull()) isOnlyUnref = true; } l = l->next; } g_list_free_full(app_infos, g_object_unref); QAction *action = new QAction(tmpIcon, qinfo.baseName(), this); connect(action, &QAction::triggered, [=]() { // automatically check for conficts CreateTemplateOperation op(m_directory, CreateTemplateOperation::Template, t); Peony::FileOperationErrorDialogWarning dlg; connect(&op, &Peony::FileOperation::errored, &dlg, &Peony::FileOperationErrorDialogWarning::handle); op.run(); auto target = op.target(); m_uris_to_edit<addAction(action); g_free(uri_str); g_object_unref(gtk_file); } subMenu->addSeparator(); } else { qWarning()<<"template entries is empty"; } } else { qWarning()<<"template path is empty"; } QList actions; auto createEmptyFileAction = new QAction(QIcon::fromTheme("document-new-symbolic"), tr("Empty File"), this); actions<addActions(actions); } return l; } const QList DirectoryViewMenu::constructViewOpActions() { QList l; if (m_selections.isEmpty()) { ViewFactorySortFilterModel2 model; model.setDirectoryUri(m_directory); auto viewNames = model.supportViewIds(); auto viewFactorysManager = DirectoryViewFactoryManager2::getInstance(); if (!viewNames.isEmpty()) { //view type; auto viewTypeAction = addAction(tr("View Type...")); l<addAction(viewFactorysManager->getFactory(viewId)->viewIcon(), viewFactorysManager->getFactory(viewId)->viewName()); viewType->setData(viewId); if (m_view->viewId() == viewId) { viewType->setCheckable(true); viewType->setChecked(true); } else { connect(viewType, &QAction::triggered, [=]() { m_top_window->beginSwitchView(viewType->data().toString()); }); } } viewTypeAction->setMenu(viewTypeSubMenu); } //sort type auto sortTypeAction = addAction(tr("Sort By...")); l< tmp; tmp<addAction(tr("Name")); tmp<addAction(tr("Modified Date")); tmp<addAction(tr("File Type")); tmp<addAction(tr("File Size")); int sortType = m_view->getSortType(); if (sortType >= 0) { tmp.at(sortType)->setCheckable(true); tmp.at(sortType)->setChecked(true); } for (int i = 0; i < tmp.count(); i++) { connect(tmp.at(i), &QAction::triggered, [=]() { m_top_window->setCurrentSortColumn(i); }); } sortTypeAction->setMenu(sortTypeMenu); //fix bug#82685 if(m_is_computer){ sortTypeAction->setEnabled(false); } //sort order auto sortOrderAction = addAction(tr("Sort Order...")); l<addAction(tr("Ascending Order")); tmp<addAction(tr("Descending 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, [=]() { m_top_window->setCurrentSortOrder(Qt::SortOrder(i)); }); } sortOrderAction->setMenu(sortOrderMenu); auto sortPreferencesAction = addAction(tr("Sort Preferences...")); l<addAction(tr("Folder First")); folderFirst->setCheckable(true); folderFirst->setChecked(m_top_window->getWindowSortFolderFirst()); connect(folderFirst, &QAction::triggered, this, [=]() { m_top_window->setSortFolderFirst(); folderFirst->setChecked(m_top_window->getWindowSortFolderFirst()); }); if (QLocale::system().name().contains("zh")) { auto useDefaultNameSortOrder = sortPreferencesMenu->addAction(tr("Chinese First")); useDefaultNameSortOrder->setCheckable(true); useDefaultNameSortOrder->setChecked(m_top_window->getWindowUseDefaultNameSortOrder()); connect(useDefaultNameSortOrder, &QAction::triggered, this, [=]() { m_top_window->setUseDefaultNameSortOrder(); bool checked = m_top_window->getWindowUseDefaultNameSortOrder(); useDefaultNameSortOrder->setChecked(checked); }); } auto showHidden = sortPreferencesMenu->addAction(tr("Show Hidden")); showHidden->setCheckable(true); showHidden->setChecked(m_top_window->getWindowShowHidden()); connect(showHidden, &QAction::triggered, this, [=]() { m_top_window->setShowHidden(); showHidden->setChecked(m_top_window->getWindowShowHidden()); }); sortPreferencesAction->setMenu(sortPreferencesMenu); } return l; } const QList DirectoryViewMenu::constructFileOpActions() { QList l; if (!m_is_trash && !m_is_search && !m_is_computer) { QString homeUri = "file://" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation); bool hasStandardPath = FileUtils::containsStandardPath(m_selections); //qDebug() << "constructFileOpActions hasStandardPath:" <isDir()) { hasDir = true; break; } } if (!hasDir) { l<repaintView(); }); } bool hasDeleteForever = false; if (!m_is_recent && !m_is_favorite && !hasStandardPath && !m_is_filesafe) { bool canTrash = true; bool canDelete = true; for (auto uri : m_selections) { auto info = FileInfo::fromUri(uri); if (! info->canTrash()) canTrash = false; if (! info->canDelete()) canDelete = false; } //fix unencrypted box file can delete to trash issue, link to bug#72948 if (canTrash && ! m_is_filebox_file) { l<editUri(m_selections.first()); }); } } else { if (!m_is_recent && !m_is_favorite && !m_is_kydroid && !m_is_filesafe) { auto pasteAction = addAction(QIcon::fromTheme("edit-paste-symbolic"), tr("Paste")); l<setEnabled(ClipboardUtils::isClipboardHasFiles()); connect(l.last(), &QAction::triggered, [=]() { auto op = ClipboardUtils::pasteClipboardFiles(m_directory); if (op) { auto window = dynamic_cast(m_top_window); auto iface = m_top_window; connect(op, &Peony::FileOperation::operationFinished, window, [=](){ auto opInfo = op->getOperationInfo(); auto targetUirs = opInfo->dests(); iface->setCurrentSelectionUris(targetUirs); }, Qt::BlockingQueuedConnection); } else{ //fix paste file in old path not update issue, link to bug#71627 m_top_window->getCurrentPage()->getView()->repaintView(); } }); } else if (m_is_recent && m_selections.count() >0) { //fix recent files can not be deleted issue l<refresh(); }); } } //select all and reverse select if (m_selections.isEmpty()) { l<invertSelections(); }); } else if(! m_is_kydroid) { l<invertSelections(); }); if (m_is_search && m_selections.count() >0) { l< DirectoryViewMenu::constructFilePropertiesActions() { QList l; //fix select mutiple file in trash path show empty issue if (m_selections.count() > 1 && (m_is_trash || m_is_recent)) return l; //favorite is should not show property if (m_selections.isEmpty() && m_directory == "favorite:///") return l; if (! m_is_search) { //包含network的情况下,不显示属性选项 if (m_is_network) { return l; } for (QString uri : m_selections) { if (uri.startsWith("network://")) { return l; } } l<setAttribute(Qt::WA_DeleteOnClose); p->show(); } else { QStringList selectUriList; if (m_selections.first().contains("favorite:///")) { for (auto uriIndex = 0; uriIndex < m_selections.count(); ++uriIndex) { if (m_selections.at(uriIndex) == "favorite:///?schema=trash" || m_selections.at(uriIndex) == "favorite:///?schema=recent") { QStringList urisList; urisList << FileUtils::getTargetUri(m_selections.at(uriIndex)); PropertiesWindow *p = new PropertiesWindow(urisList); p->setAttribute(Qt::WA_DeleteOnClose); p->show(); } else { selectUriList<< m_selections.at(uriIndex); } } }else { selectUriList = m_selections; } if (selectUriList.count() > 0) { PropertiesWindow *p = new PropertiesWindow(selectUriList); p->setAttribute(Qt::WA_DeleteOnClose); p->show(); } } }); } else if (m_selections.count() == 1) { l<setAttribute(Qt::WA_DeleteOnClose); p->show(); }); } return l; } const QList DirectoryViewMenu::constructComputerActions() { QList l; if (m_is_computer && m_selections.count() == 1) { QString uri = m_selections.first(); auto info = FileInfo::fromUri(uri); if (info->displayName().isEmpty() || info->targetUri().isEmpty()) { FileInfoJob j(info); j.querySync(); } auto mount = VolumeManager::getMountFromUri(info->targetUri()); //fix bug#52491, CDROM and DVD can format issue if (nullptr != mount) { QString unixDevice = FileUtils::getUnixDevice(info->uri()); if (! unixDevice.isNull() && ! unixDevice.contains("/dev/sr") && info->isVolume() && info->canUnmount()) { l<uri(), nullptr, m_view); fd->show(); }); if (!mount) { l.last()->setEnabled(false); } } } } return l; } const QList DirectoryViewMenu::constructTrashActions() { QList l; if (m_is_trash) { bool isTrashEmpty = m_top_window->getCurrentAllFileUris().isEmpty(); if (m_selections.isEmpty()) { l<setEnabled(!isTrashEmpty); connect(l.last(), &QAction::triggered, [=]() { AudioPlayManager::getInstance()->playWarningAudio(); auto result = QMessageBox::question(nullptr, tr("Delete Permanently"), tr("Are you sure that you want to delete these files? " "Once you start a deletion, the files deleting will never be " "restored again.")); if (result == QMessageBox::Yes) { auto uris = m_top_window->getCurrentAllFileUris(); FileOperationUtils::remove(uris); } }); } else { l<playWarningAudio(); auto result = QMessageBox::question(nullptr, tr("Delete Permanently"), tr("Are you sure that you want to delete these files? " "Once you start a deletion, the files deleting will never be " "restored again.")); if (result == QMessageBox::Yes) { FileOperationUtils::remove(m_selections); } }); } } else if (m_is_recent && m_selections.isEmpty()) { l<clearAll(); }); } return l; } const QList DirectoryViewMenu::constructSearchActions() { QList l; if (m_is_search || m_is_recent) { if (m_selections.isEmpty()) return l; l<create(parentUri); auto newWindow = dynamic_cast(windowIface); auto selection = m_selections; #if QT_VERSION > QT_VERSION_CHECK(5, 12, 0) QTimer::singleShot(1000, newWindow, [=]() { if (newWindow) windowIface->setCurrentSelectionUris(selection); }); #else QTimer::singleShot(1000, [=]() { if (newWindow) windowIface->setCurrentSelectionUris(selection); }); #endif newWindow->show(); } else { QMessageBox::warning(nullptr, tr("Error"), tr("File:\"%1\" is not exist, did you moved or deleted it?").arg(QUrl(uri).path())); } } }); } return l; } const QList DirectoryViewMenu::constructMenuPluginActions() { QList l; if (!m_is_favorite) { auto pluginIds = MenuPluginManager::getInstance()->getPluginIds(); //sort plugiins by name, so the menu option orders is relatively fixed std::sort(pluginIds.begin(), pluginIds.end()); for (auto id : pluginIds) { auto plugin = MenuPluginManager::getInstance()->getPlugin(id); if(m_is_filesafe||m_is_filebox_file) { if(plugin->name() == tr("Peony-Qt filesafe menu Extension")) { auto actions = plugin->menuActions(MenuPluginInterface::DirectoryView, m_directory, m_selections); l<setParent(this); addAction(action); } } } else { if(plugin->name() != tr("Peony-Qt filesafe menu Extension")) { auto actions = plugin->menuActions(MenuPluginInterface::DirectoryView, m_directory, m_selections); l<setParent(this); addAction(action); } } } } } return l; } peony/libpeony-qt/controls/menu/directory-view-menu/directory-view-menu.pri0000644000175000017500000000016314205101223026240 0ustar fengfengINCLUDEPATH += $$PWD HEADERS += \ $$PWD/directory-view-menu.h SOURCES += \ $$PWD/directory-view-menu.cpp peony/libpeony-qt/controls/menu/side-bar-menu/0000755000175000017500000000000014205115226020330 5ustar fengfengpeony/libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.cpp0000644000175000017500000002141414205115226023466 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "side-bar-menu.h" #include "side-bar-abstract-item.h" #include "bookmark-manager.h" #include "properties-window.h" #include "menu-plugin-manager.h" #include "file-utils.h" #include "file-info.h" #include "file-info-job.h" #include #include #include #include #include using namespace Peony; static const int FAVORITE_CAN_NOT_DELETE_URI_COUNT=7; SideBarMenu::SideBarMenu(SideBarAbstractItem *item, SideBar *sideBar, QWidget *parent) : QMenu (parent) { m_uri = item->uri(); m_item = item; m_side_bar = sideBar; if (!item) { auto action = addAction(QIcon::fromTheme("preview-file"), tr("Properties")); action->setEnabled(false); return; } switch (item->type()) { case SideBarAbstractItem::FavoriteItem: { constructFavoriteActions(); break; } case SideBarAbstractItem::PersonalItem: { constructPersonalActions(); break; } case SideBarAbstractItem::FileSystemItem: { constructFileSystemItemActions(); break; } case SideBarAbstractItem::NetWorkItem: { constructNetWorkItemActions(); break; } default: { auto action = addAction(QIcon::fromTheme("preview-file"), tr("Properties")); action->setEnabled(false); break; } } } const QList SideBarMenu::constructFavoriteActions() { QList l; l<removeBookMark(m_uri); }); if (!m_item->firstColumnIndex().parent().isValid()) { l.last()->setEnabled(false); } else if (m_item->firstColumnIndex().row()setEnabled(false); } else if (m_uri == "favorite:///data/usershare?schema=file" || m_uri == "kmre:///" || m_uri == "kydroid:///") { //fix bug#68431, can not delete option issue l.last()->setEnabled(false); } l<show(); }); if (!m_item->firstColumnIndex().parent().isValid()) { l.last()->setEnabled(false); } return l; } const QList SideBarMenu::constructPersonalActions() { QList l; l<show(); }); return l; } #include "file-enumerator.h" const QList SideBarMenu::constructFileSystemItemActions() { QList l; /* 卸载 */ if (m_item->isUnmountable()) { l<unmount(); }); l.last()->setEnabled(m_item->isMounted()); } /* 弹出 */ if (m_item->isEjectable()||m_item->isStopable()) { l<eject(G_MOUNT_UNMOUNT_NONE); }); //l.last()->setEnabled(m_item->isMounted()); } QString unixDevice = m_item->getDevice(); QString uri; if(m_uri=="file:///") /* 文件系统特殊处理 */ uri = "computer:///root.link"; else if(!unixDevice.isEmpty())/* 由于格式化、属性、插件(例如:发送到移动设备)等未重构,还是需要用之前的computer uri */ uri = getComputerUriFromUnixDevice(unixDevice); else uri=m_uri; /* 光盘暂时没有格式化功能以及手机要求不能格式化 */ if((! unixDevice.isNull() && ! unixDevice.contains("/dev/sr")) &&!unixDevice.startsWith("/dev/bus/usb") && (m_item->isMountable()||m_item->isUnmountable())) { l<show(); }); } /* 插件 */ if(0 != QString::compare(m_uri, "filesafe:///")) { auto mgr = MenuPluginManager::getInstance(); auto ids = mgr->getPluginIds(); for (auto id : ids) { auto factory = mgr->getPlugin(id); //qDebug()<menuActions(MenuPluginInterface::SideBar, uri, QStringList()<setParent(this); } l<show(); } }); if ((0 != QString::compare(m_uri, "computer:///")) && (0 != QString::compare(m_uri, "filesafe:///")) &&(m_item->isMountable()||m_item->isUnmountable())) { l.last()->setEnabled(m_item->isMounted()); } return l; } void SideBarMenu::gotoAboutComputer() { QProcess p; p.setProgram("ukui-control-center"); //-a para to show about computer infos p.setArguments(QStringList()<<"-a"); #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) p.startDetached(); #else p.startDetached("ukui-control-center", QStringList()<<"-a"); #endif p.waitForFinished(-1); } const QList SideBarMenu::constructNetWorkItemActions() { QList l; static const QString netWorkUri="network:///"; /* 共享文件夹无右键菜单'卸载' */ if (!m_uri.startsWith("file://")) { l<unmount(); }); l.last()->setEnabled(m_item->isMounted()); } if(netWorkUri != m_uri){ l<isMountable()||m_item->isUnmountable())){ /* 远程服务器 */ FileEnumerator e; e.setEnumerateDirectory("computer:///"); e.enumerateSync(); for (auto fileInfo : e.getChildren()) { FileInfoJob infoJob(fileInfo); infoJob.querySync(); /* 由远程服务器的targeturi获取uri来调用属性窗口, */ QUrl targetUrl(fileInfo.get()->targetUri()); QUrl sourceUrl(m_uri); if(sourceUrl.scheme()==targetUrl.scheme() && sourceUrl.host()==targetUrl.host()){/* 相同scheme和host,但port不同时怎么处理呢? */ QString uri = fileInfo.get()->uri(); PropertiesWindow *w = new PropertiesWindow(QStringList()<show(); break; } } }else{ /* 共享文件夹 */ PropertiesWindow *w = new PropertiesWindow(QStringList()<show(); } }); if(m_item->isMountable()||m_item->isUnmountable()) l.last()->setEnabled(m_item->isMounted()); } return l; } QString SideBarMenu::getComputerUriFromUnixDevice(const QString &unixDevice){ /* volume item,遍历方式获取uri */ FileEnumerator e; e.setEnumerateDirectory("computer:///"); e.enumerateSync(); QString uri; for (auto fileInfo : e.getChildren()) { FileInfoJob infoJob(fileInfo); infoJob.querySync(); /* 由volume的unixDevice获取computer uir */ auto info = infoJob.getInfo(); QString device = fileInfo.get()->unixDeviceFile(); if(device==unixDevice){ uri = fileInfo.get()->uri(); break; } } return uri; } peony/libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.pri0000644000175000017500000000014714205101223023466 0ustar fengfengINCLUDEPATH += $$PWD HEADERS += \ $$PWD/side-bar-menu.h SOURCES += \ $$PWD/side-bar-menu.cpp peony/libpeony-qt/controls/menu/side-bar-menu/side-bar-menu.h0000644000175000017500000000306314205101223023123 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef SIDEBARMENU_H #define SIDEBARMENU_H #include #include "peony-core_global.h" namespace Peony { class SideBarAbstractItem; class SideBar; class PEONYCORESHARED_EXPORT SideBarMenu : public QMenu { Q_OBJECT public: explicit SideBarMenu(SideBarAbstractItem *item, SideBar *sideBar, QWidget *parent = nullptr); void gotoAboutComputer(); protected: const QList constructFavoriteActions(); const QList constructPersonalActions(); const QList constructFileSystemItemActions(); const QList constructNetWorkItemActions(); private: QString getComputerUriFromUnixDevice(const QString& unixDevice); private: SideBarAbstractItem *m_item; SideBar *m_side_bar; QString m_uri; }; } #endif // SIDEBARMENU_H peony/libpeony-qt/controls/menu/menu-iface/0000755000175000017500000000000014205101223017701 5ustar fengfengpeony/libpeony-qt/controls/menu/menu-iface/menu-iface.pri0000644000175000017500000000002514205101223022423 0ustar fengfengINCLUDEPATH += $$PWD peony/libpeony-qt/controls/side-bar/0000755000175000017500000000000014205115226016422 5ustar fengfengpeony/libpeony-qt/controls/side-bar/side-bar.pri0000644000175000017500000000013714205115226020625 0ustar fengfengINCLUDEPATH += $$PWD HEADERS += \ $$PWD/side-bar.h \ SOURCES += \ $$PWD/side-bar.cpp peony/libpeony-qt/controls/side-bar/side-bar.h0000644000175000017500000000277114205115226020270 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef SIDEBAR_H #define SIDEBAR_H #include #include "peony-core_global.h" namespace Peony { class SideBarDelegate; class SideBar : public QTreeView { friend class SideBarDelegate; Q_OBJECT public: explicit SideBar(QWidget *parent = nullptr); QSize sizeHint() const override; Q_SIGNALS: void updateWindowLocationRequest(const QString &uri, bool addHistory = true, bool forceUpdate = false); protected: void paintEvent(QPaintEvent *e) override; QRect visualRect(const QModelIndex &index) const override; //int horizontalOffset() const override {return 100;} void dragEnterEvent(QDragEnterEvent *e) override; void dragMoveEvent(QDragMoveEvent *e) override; }; } #endif // SIDEBAR_H peony/libpeony-qt/controls/side-bar/side-bar.cpp0000644000175000017500000001045014205115226020614 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "side-bar.h" #include "side-bar-model.h" #include "side-bar-proxy-filter-sort-model.h" #include "side-bar-abstract-item.h" #include "side-bar-delegate.h" #include "side-bar-menu.h" #include #include #include #include #include #include #include using namespace Peony; SideBar::SideBar(QWidget *parent) : QTreeView(parent) { setDropIndicatorShown(false); setAttribute(Qt::WA_Hover); connect(qApp, &QApplication::paletteChanged, this, [=]() { this->update(); this->viewport()->update(); }); setContextMenuPolicy(Qt::CustomContextMenu); setDragDropMode(QTreeView::DragDrop); setIndentation(15); setSelectionBehavior(QTreeView::SelectRows); setContentsMargins(0, 0, 0, 0); setItemDelegate(new SideBarDelegate(this)); auto model = new SideBarModel(this); auto proxy_model = new SideBarProxyFilterSortModel(model); setSortingEnabled(true); setExpandsOnDoubleClick(false); //don't show HorizontalScroll setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); header()->setSectionResizeMode(QHeaderView::ResizeToContents); header()->setVisible(false); setModel(proxy_model); proxy_model->setSourceModel(model); connect(this, &QTreeView::expanded, [=](const QModelIndex &index) { auto item = proxy_model->itemFromIndex(index); item->findChildrenAsync(); }); connect(this, &QTreeView::collapsed, [=](const QModelIndex &index) { auto item = proxy_model->itemFromIndex(index); item->clearChildren(); }); connect(this, &QTreeView::clicked, [=](const QModelIndex &index) { switch (index.column()) { case 0: { auto item = proxy_model->itemFromIndex(index); //some side bar item doesn't have a uri. //do not emit signal with a null uri to window. if (!item->uri().isNull()) Q_EMIT this->updateWindowLocationRequest(item->uri()); break; } case 1: { auto item = proxy_model->itemFromIndex(index); if (item->isMounted() && item->isRemoveable()) { auto leftIndex = proxy_model->index(index.row(), 0, index.parent()); this->collapse(leftIndex); item->unmount(); } break; } default: break; } }); connect(this, &QTreeView::customContextMenuRequested, this, [=](const QPoint &pos) { auto index = indexAt(pos); auto item = proxy_model->itemFromIndex(index); if (item) { if (item->type() != SideBarAbstractItem::SeparatorItem) { SideBarMenu menu(item, this); menu.exec(QCursor::pos()); } } }); expandAll(); } QSize SideBar::sizeHint() const { auto size = QTreeView::sizeHint(); size.setWidth(180); return size; } void SideBar::paintEvent(QPaintEvent *e) { QTreeView::paintEvent(e); } QRect SideBar::visualRect(const QModelIndex &index) const { return QTreeView::visualRect(index); } void SideBar::dragEnterEvent(QDragEnterEvent *e) { //qDebug()<<"enter"; e->accept(); setState(DraggingState); } void SideBar::dragMoveEvent(QDragMoveEvent *e) { //qDebug()<<"move"; auto widget = static_cast(e->source()); if (widget) { if (widget->topLevelWidget() == this->topLevelWidget()) { QTreeView::dragMoveEvent(e); } } e->accept(); } peony/libpeony-qt/controls/icon-container.cpp0000644000175000017500000000430314205113763020354 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "icon-container.h" #include #include #include #include using namespace Peony; IconContainer::IconContainer(QWidget *parent) : QPushButton(parent) { setEnabled(true); setCheckable(false); setDefault(true); setFlat(true); m_style = new IconContainerStyle; setStyle(m_style); //fix push button use as icon caused color issues this->setProperty("isIcon", true); setAttribute(Qt::WA_TranslucentBackground); auto shadowEffect = new QGraphicsDropShadowEffect; shadowEffect->setBlurRadius(20); shadowEffect->setOffset(0, 0); setGraphicsEffect(shadowEffect); } IconContainer::~IconContainer() { m_style->deleteLater(); } void IconContainer::paintEvent(QPaintEvent *e) { //QPainter p(this); //p.fillRect(this->rect(), this->palette().base()); //p.setPen(this->palette().dark().color()); //p.drawRect(this->rect().adjusted(0, 0, -1, -1)); QPushButton::paintEvent(e); } IconContainerStyle::IconContainerStyle() : QProxyStyle() { } void IconContainerStyle::drawControl(QStyle::ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { QStyleOptionButton opt = *qstyleoption_cast(option); opt.palette.setColor(QPalette::Highlight, Qt::transparent); return QProxyStyle::drawControl(element, &opt, painter, widget); } peony/libpeony-qt/controls/directory-view/0000755000175000017500000000000014205115226017710 5ustar fengfengpeony/libpeony-qt/controls/directory-view/directory-view-widget.cpp0000644000175000017500000000210514205115226024647 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "directory-view-widget.h" #include using namespace Peony; DirectoryViewWidget::DirectoryViewWidget(QWidget *parent) : QWidget (parent) { setAttribute(Qt::WA_DeleteOnClose); connect(qApp, &QApplication::paletteChanged, this, [=]() { this->repaintView(); }); } peony/libpeony-qt/controls/directory-view/view/0000755000175000017500000000000014205101223020652 5ustar fengfengpeony/libpeony-qt/controls/directory-view/view/list-view/0000755000175000017500000000000014205115226022605 5ustar fengfengpeony/libpeony-qt/controls/directory-view/view/list-view/list-view.h0000644000175000017500000001550714205115226024711 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef LISTVIEW_H #define LISTVIEW_H #include #include "directory-view-plugin-iface.h" #include "peony-core_global.h" #include "directory-view-widget.h" #include namespace Peony { class FileItemModel; class FileItemProxyFilterSortModel; namespace DirectoryView { /*! * \brief The ListView class * \todo * improve extend selection actions. */ class PEONYCORESHARED_EXPORT ListView : public QTreeView, public DirectoryViewIface { friend class ListView2; Q_OBJECT public: explicit ListView(QWidget *parent = nullptr); void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) override; const QString viewId() override { return tr("List View"); } bool isDragging(); void bindModel(FileItemModel *sourceModel, FileItemProxyFilterSortModel *proxyModel) override; void setProxy(DirectoryViewProxyIface *proxy) override; /*! * \brief getProxy * \return * \deprecated */ DirectoryViewProxyIface *getProxy() override; //location const QString getDirectoryUri() override; //selections const QStringList getSelections() override; //children const QStringList getAllFileUris() override; QRect visualRect(const QModelIndex &index) const override; Q_SIGNALS: void zoomLevelChangedRequest(bool zoomIn); public Q_SLOTS: //location void open(const QStringList &uris, bool newWindow) override; void setDirectoryUri(const QString &uri) override; void beginLocationChange() override; void stopLocationChange() override; void closeView() override; //selections void setSelections(const QStringList &uris) override; void invertSelections() override; void scrollToSelection(const QString &uri) override; //clipboard void setCutFiles(const QStringList &uris) override; int getSortType() override; void setSortType(int sortType) override; int getSortOrder() override; void setSortOrder(int sortOrder) override; void editUri(const QString &uri) override; void editUris(const QStringList uris) override; void keyboardSearch(const QString &key) override; void resort(); void reportViewDirectoryChanged(); void adjustColumnsSize(); void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles); protected: void mousePressEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mouseDoubleClickEvent(QMouseEvent *event) override; void keyPressEvent(QKeyEvent *e) override; void keyReleaseEvent(QKeyEvent *e) override; void dragEnterEvent(QDragEnterEvent *e) override; void dragMoveEvent(QDragMoveEvent *e) override; void dropEvent(QDropEvent *e) override; void resizeEvent(QResizeEvent *e) override; void updateGeometries() override; void reUpdateScrollBar(); void wheelEvent (QWheelEvent *e) override; void focusInEvent(QFocusEvent *e) override; void startDrag(Qt::DropActions flags) override; private Q_SLOTS: void slotRename(); private: FileItemModel *m_model = nullptr; FileItemProxyFilterSortModel *m_proxy_model = nullptr; QTimer* m_renameTimer; bool m_editValid; bool m_ctrl_key_pressed; QRubberBand *m_rubberBand; QPoint m_lastPressedLogicPoint; QRect m_logicRect; bool m_isLeftButtonPressed = false; QModelIndex m_last_index; DirectoryViewProxyIface *m_proxy = nullptr; QString m_current_uri; QSize m_last_size; const int BOTTOM_STATUS_MARGIN = 200; }; //ListView2 class ListView2 : public DirectoryViewWidget { Q_OBJECT //internal plugin public: explicit ListView2(QWidget *parent = nullptr); ~ListView2(); const QString viewId() { return "List View"; } //location const QString getDirectoryUri() { return m_view->getDirectoryUri(); } //selections const QStringList getSelections() { return m_view->getSelections(); } //children const QStringList getAllFileUris() { return m_view->getAllFileUris(); } int getSortType() { return m_view->getSortType(); } Qt::SortOrder getSortOrder() { return Qt::SortOrder(m_view->getSortOrder()); } int currentZoomLevel() { return m_zoom_level; } int minimumZoomLevel() { return 0; } int maximumZoomLevel() { return 20; } bool supportZoom() { return true; } public Q_SLOTS: void bindModel(FileItemModel *model, FileItemProxyFilterSortModel *proxyModel); //location //void open(const QStringList &uris, bool newWindow); void setDirectoryUri(const QString &uri) { m_need_resize_header = true; m_view->setDirectoryUri(uri); } void beginLocationChange() { m_view->beginLocationChange(); } void stopLocationChange() { m_view->stopLocationChange(); } void closeDirectoryView() { m_view->closeView(); } //selections void setSelections(const QStringList &uris) { m_view->setSelections(uris); } void invertSelections() { m_view->invertSelections(); } void scrollToSelection(const QString &uri) { m_view->scrollToSelection(uri); } //clipboard //cut items should be drawn differently. void setCutFiles(const QStringList &uris) { m_view->setCutFiles(uris); } void setSortType(int sortType) { m_view->setSortType(sortType); } void setSortOrder(int sortOrder) { m_view->setSortOrder(sortOrder); } void editUri(const QString &uri) { m_view->editUri(uri); } void editUris(const QStringList uris) { m_view->editUris(uris); } void setCurrentZoomLevel(int zoomLevel); void clearIndexWidget(); void repaintView(); private: ListView *m_view = nullptr; FileItemModel *m_model = nullptr; FileItemProxyFilterSortModel *m_proxy_model = nullptr; int m_zoom_level = 20; bool m_need_resize_header; }; } } #endif // LISTVIEW_H peony/libpeony-qt/controls/directory-view/view/list-view/list-view.pri0000644000175000017500000000023514205101223025234 0ustar fengfengINCLUDEPATH += $$PWD HEADERS += \ $$PWD/list-view-style.h \ $$PWD/list-view.h SOURCES += \ $$PWD/list-view-style.cpp \ $$PWD/list-view.cpp peony/libpeony-qt/controls/directory-view/view/list-view/list-view-style.cpp0000644000175000017500000000361614205115226026400 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #include "list-view-style.h" #include #include #include using namespace Peony; using namespace Peony::DirectoryView; static ListViewStyle *global_instance = nullptr; ListViewStyle::ListViewStyle(QObject *parent) : QProxyStyle() { } ListViewStyle *ListViewStyle::getStyle() { if (!global_instance) global_instance = new ListViewStyle; return global_instance; } void ListViewStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { if (element == PE_Frame) { painter->save(); bool isActive = option->state & State_Active; bool isEnable = option->state & State_Enabled; auto baseColor = option->palette.color(isEnable? (isActive? QPalette::Active: QPalette::Inactive): QPalette::Disabled, QPalette::Window); painter->fillRect(widget->rect(), baseColor); painter->restore(); return; } if (element == PE_FrameWindow) { return; } return QProxyStyle::drawPrimitive(element, option, painter, widget); } peony/libpeony-qt/controls/directory-view/view/list-view/list-view-style.h0000644000175000017500000000250614205113763026046 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #ifndef LISTVIEWSTYLE_H #define LISTVIEWSTYLE_H #include namespace Peony { namespace DirectoryView { class ListViewStyle : public QProxyStyle { Q_OBJECT public: static ListViewStyle *getStyle(); void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const override; private: explicit ListViewStyle(QObject *parent = nullptr); }; } } #endif // LISTVIEWSTYLE_H peony/libpeony-qt/controls/directory-view/view/list-view/list-view.cpp0000644000175000017500000007066114205115226025246 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #include "list-view.h" #include "file-item-model.h" #include "file-item-proxy-filter-sort-model.h" #include "list-view-delegate.h" #include "file-item.h" #include "file-utils.h" #include "file-info.h" #include "list-view-style.h" #include "global-settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Peony; using namespace Peony::DirectoryView; ListView::ListView(QWidget *parent) : QTreeView(parent) { // use scroll per pixel mode for calculate vertical scroll bar range. // see reUpdateScrollBar() setVerticalScrollMode(ScrollPerPixel); setAttribute(Qt::WA_TranslucentBackground); setStyle(Peony::DirectoryView::ListViewStyle::getStyle()); setAutoScroll(true); setAutoScrollMargin(100); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setSelectionBehavior(QTreeView::SelectRows); setAlternatingRowColors(true); setAutoFillBackground(true); setBackgroundRole(QPalette::Base); setItemDelegate(new ListViewDelegate(this)); header()->setSectionResizeMode(QHeaderView::Interactive); header()->setSectionsMovable(true); //header()->setStretchLastSection(true); setExpandsOnDoubleClick(false); setSortingEnabled(true); setEditTriggers(QTreeView::NoEditTriggers); setDragEnabled(true); setDragDropMode(QTreeView::DragDrop); setSelectionMode(QTreeView::ExtendedSelection); //setAlternatingRowColors(true); //setContextMenuPolicy(Qt::CustomContextMenu); m_renameTimer = new QTimer(this); m_renameTimer->setInterval(3000); m_editValid = false; //use this property to fix bug 44314 and 33558 //bug#42244 need to find and fix update fail issue setUniformRowHeights(true); setIconSize(QSize(40, 40)); setMouseTracking(true);//追踪鼠标 m_rubberBand = new QRubberBand(QRubberBand::Shape::Rectangle, this); } void ListView::scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint hint) { // Note: due to we rewrite the calculation of view scroll area based on current process, // we could not use QTreeView::scrollTo in some cases because it still based on old process // and will conflict with new calculation. Q_UNUSED(index) Q_UNUSED(hint) reUpdateScrollBar(); } bool ListView::isDragging() { return state() == QAbstractItemView::DraggingState; } void ListView::bindModel(FileItemModel *sourceModel, FileItemProxyFilterSortModel *proxyModel) { if (!sourceModel || !proxyModel) return; m_model = sourceModel; m_proxy_model = proxyModel; m_proxy_model->setSourceModel(m_model); setModel(proxyModel); //adjust columns layout. adjustColumnsSize(); //fix diffcult to unselect all item issue // connect(this->selectionModel(), &QItemSelectionModel::currentColumnChanged, [=] // (const QModelIndex ¤t, const QModelIndex &previous) { // qDebug()<<"list view currentColumnChanged changed"; // if (getSelections().count() > 1 && !m_ctrl_key_pressed) // { // this->clearSelection(); // if (current.isValid()) // setCurrentIndex(current); // } // }); // connect(this->selectionModel(), &QItemSelectionModel::currentRowChanged, [=] // (const QModelIndex ¤t, const QModelIndex &previous) { // qDebug()<<"list view currentRowChanged changed"; // if (getSelections().count() > 1 && !m_ctrl_key_pressed) // { // this->clearSelection(); // if (current.isValid()) // setCurrentIndex(current); // } // }); //edit trigger connect(this->selectionModel(), &QItemSelectionModel::selectionChanged, [=](const QItemSelection &selection, const QItemSelection &deselection) { qDebug()<<"list view selection changed"; auto currentSelections = selection.indexes(); for (auto index : deselection.indexes()) { this->setIndexWidget(index, nullptr); } //rename trigger if (!currentSelections.isEmpty()) { int first_index_row = currentSelections.first().row(); bool all_index_in_same_row = true; for (auto index : currentSelections) { if (first_index_row != index.row()) { all_index_in_same_row = false; break; } } if (all_index_in_same_row) { if(m_last_index.row() != currentSelections.first().row()) { m_editValid = false; } m_last_index = currentSelections.first(); } } else { m_last_index = QModelIndex(); m_editValid = false; } }); } void ListView::keyPressEvent(QKeyEvent *e) { QTreeView::keyPressEvent(e); switch (e->key()) { case Qt::Key_Control: m_ctrl_key_pressed = true; break; case Qt::Key_Up: { if (!selectedIndexes().isEmpty()) { QTreeView::scrollTo(selectedIndexes().first()); } break; } case Qt::Key_Down: { if (!selectedIndexes().isEmpty()) { auto index = selectedIndexes().first(); if (index.row() + 1 == model()->rowCount()) { verticalScrollBar()->setValue(qMin(verticalScrollBar()->value() + iconSize().height(), verticalScrollBar()->maximum())); } else { QTreeView::scrollTo(selectedIndexes().first()); } } break; } default: break; } } void ListView::keyReleaseEvent(QKeyEvent *e) { QTreeView::keyReleaseEvent(e); if (e->key() == Qt::Key_Control) m_ctrl_key_pressed = false; } void ListView::mousePressEvent(QMouseEvent *e) { if (e->button() == Qt::RightButton) { if (this->state() == QTreeView::EditingState) { if (indexWidget(indexAt(e->pos()))) return; } Q_EMIT customContextMenuRequested(e->pos()); m_rubberBand->hide(); return; } m_isLeftButtonPressed = true; m_rubberBand->hide(); m_lastPressedLogicPoint = e->pos() + QPoint(horizontalOffset(), verticalOffset()); auto index = indexAt(e->pos()); bool isIndexSelected = selectedIndexes().contains(index); m_editValid = true; QTreeView::mousePressEvent(e); auto visualRect = this->visualRect(index); auto sizeHint = itemDelegate()->sizeHint(viewOptions(), index); auto validRect = QRect(visualRect.topLeft(), sizeHint); if (!validRect.contains(e->pos())) { if (isIndexSelected) { clearSelection(); setCurrentIndex(index); } this->setState(QAbstractItemView::DragSelectingState); } //comment to fix can not enter rename issue // else if (isIndexSelected) { // return; // } //if click left button at blank space, it should select nothing //qDebug() << "indexAt(e->pos()):" <pos()).column() << indexAt(e->pos()).row() <pos()).isValid(); if(e->button() == Qt::LeftButton && (!indexAt(e->pos()).isValid()) ) { this->clearSelection(); //this->clearFocus(); return; } //m_renameTimer if(!m_renameTimer->isActive()) { m_renameTimer->start(); m_editValid = false; } else { //if remain time is between[0.75, 3000],then trigger rename event; //to make sure only click one row bool all_index_in_same_row = true; if (!this->selectedIndexes().isEmpty()) { int first_index_row = this->selectedIndexes().first().row(); for (auto index : this->selectedIndexes()) { if (first_index_row != index.row()) { all_index_in_same_row = false; break; } } } //qDebug()<remainingTime()<styleHints()->mouseDoubleClickInterval(); if(m_renameTimer->remainingTime()>=0 && m_renameTimer->remainingTime() <= 3000 - qApp->styleHints()->mouseDoubleClickInterval() && indexAt(e->pos()) == m_last_index && m_last_index.isValid() && m_editValid == true && all_index_in_same_row) { slotRename(); } else { m_editValid = false; } } } void ListView::mouseReleaseEvent(QMouseEvent *e) { QTreeView::mouseReleaseEvent(e); m_rubberBand->hide(); m_isLeftButtonPressed = false; } 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(); } } QTreeView::mouseMoveEvent(e); if (e->buttons() & Qt::LeftButton) { auto pos = e->pos(); auto offset = QPoint(horizontalOffset(), verticalOffset()); auto logicPos = pos + offset; QRect logicRect = QRect(logicPos, m_lastPressedLogicPoint); m_logicRect = logicRect.normalized(); int dx = -horizontalOffset(); int dy = -verticalOffset() + this->header()->height(); auto realRect = m_logicRect.adjusted(dx, dy, dx ,dy); if (!m_rubberBand->isVisible()) m_rubberBand->show(); m_rubberBand->setGeometry(realRect); } else { m_rubberBand->hide(); } } void ListView::mouseDoubleClickEvent(QMouseEvent *event) { m_editValid = false; QTreeView::mouseDoubleClickEvent(event); } void ListView::dragEnterEvent(QDragEnterEvent *e) { m_editValid = false; qDebug()<<"dragEnterEvent()"; //QTreeView::dragEnterEvent(e); 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; qDebug()<<"dragEnterEvent()" <mimeData()->hasUrls()) { e->setDropAction(action); e->accept(); } } void ListView::dragMoveEvent(QDragMoveEvent *e) { 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; //qDebug()<<"list view dragMoveEvent()" <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 (this == e->source()) { return QTreeView::dragMoveEvent(e); } e->setDropAction(action); e->accept(); } void ListView::dropEvent(QDropEvent *e) { // do not comment this code. if (e->source() == this) { // only handle the drop event on item. switch (dropIndicatorPosition()) { case QAbstractItemView::DropIndicatorPosition::OnItem: { break; } case QAbstractItemView::DropIndicatorPosition::OnViewport: { if (e->keyboardModifiers() & Qt::ControlModifier) { break; } else { return; } } default: return; } } // QTreeView::dropEvent(e); m_last_index = QModelIndex(); //m_edit_trigger_timer.stop(); 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; e->setDropAction(action); if (e->keyboardModifiers() & Qt::ShiftModifier) { action = Qt::TargetMoveAction; } auto proxy_index = indexAt(e->pos()); auto index = m_proxy_model->mapToSource(proxy_index); qDebug()<<"dropEvent" <pos()).isValid(); //move in current path, do nothing if (e->source() == this) { if (indexAt(e->pos()).isValid()) { auto uri = m_proxy_model->itemFromIndex(proxy_index)->uri(); if(!e->mimeData()->urls().contains(uri)) m_model->dropMimeData(e->mimeData(), action, 0, 0, index); } else { if (m_ctrl_key_pressed) { m_model->dropMimeData(e->mimeData(), Qt::CopyAction, 0, 0, QModelIndex()); } } return; } m_model->dropMimeData(e->mimeData(), action, 0, 0, index); } void ListView::resizeEvent(QResizeEvent *e) { QTreeView::resizeEvent(e); if (m_last_size != size()) { m_last_size = size(); adjustColumnsSize(); } } /*! * \brief ListView::reUpdateScrollBar * \details * tree view use QTreeViewPrivate::updateScrollBars() for reset scrollbar range. * there are 3 parts for that method called. * * 1. QTreeView::scrollTo() * 2. QTreeView::dataChanged() * 3. QTreeView::updateGeometries() * * we have to override all of them to make sure that our custom scrollbar range * set correctly. */ void ListView::reUpdateScrollBar() { if (!model()) return; if (model()->rowCount() == 0) { return; } int totalHeight = 0; int rowCount = model()->rowCount(); for (int row = 0; row < rowCount; row++) { auto index = model()->index(row, 0); totalHeight += sizeHintForIndex(index).height(); } verticalScrollBar()->setSingleStep(iconSize().height()); verticalScrollBar()->setPageStep(viewport()->height() - header()->height()); verticalScrollBar()->setRange(0, totalHeight + header()->height() + 100 - viewport()->height()); } void ListView::updateGeometries() { QTreeView::updateGeometries(); reUpdateScrollBar(); } void ListView::wheelEvent(QWheelEvent *e) { if (e->modifiers() & Qt::ControlModifier) { zoomLevelChangedRequest(e->delta() > 0); return; } QTreeView::wheelEvent(e); } void ListView::focusInEvent(QFocusEvent *e) { QTreeView::focusInEvent(e); if (e->reason() == Qt::TabFocus) { if (selectedIndexes().isEmpty()) { selectionModel()->select(model()->index(0, 0), QItemSelectionModel::SelectCurrent|QItemSelectionModel::Rows); } else { QTreeView::scrollTo(selectedIndexes().first(), QTreeView::EnsureVisible); reUpdateScrollBar(); auto selections = selectedIndexes(); clearSelection(); QTimer::singleShot(100, this, [=](){ for (auto index : selections) { selectionModel()->select(index, QItemSelectionModel::Select); } }); } } } void ListView::startDrag(Qt::DropActions flags) { auto indexes = selectedIndexes(); if (indexes.count() > 0) { auto pos = mapFromGlobal(QCursor::pos()); 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 indexRectHash; for (auto index : indexes) { rect += (visualRect(index)); indexRectHash.insert(index, visualRect(index)); } QRect realRect = rect.boundingRect(); QPixmap pixmap(realRect.size() * scale); pixmap.fill(Qt::transparent); pixmap.setDevicePixelRatio(scale); QPainter painter(&pixmap); for (auto index : indexes) { painter.save(); painter.translate(indexRectHash.value(index).topLeft() - rect.boundingRect().topLeft()); //painter.translate(-rect.boundingRect().topLeft()); QStyleOptionViewItem opt = viewOptions(); auto viewItemDelegate = static_cast(itemDelegate()); viewItemDelegate->initIndexOption(&opt, index); opt.displayAlignment = Qt::Alignment(Qt::AlignLeft|Qt::AlignVCenter); opt.rect.setSize(indexRectHash.value(index).size()); opt.rect.moveTo(0, 0); opt.state |= QStyle::State_Selected; painter.setOpacity(0.8); QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, &painter); painter.restore(); } drag->setPixmap(pixmap); drag->setHotSpot(pos - rect.boundingRect().topLeft() - QPoint(0, header()->height())); drag->setDragCursor(QPixmap(), m_ctrl_key_pressed? Qt::CopyAction: Qt::MoveAction); drag->exec(m_ctrl_key_pressed? Qt::CopyAction: Qt::MoveAction); } } void ListView::slotRename() { //special path like trash path not allow rename if (getDirectoryUri().startsWith("trash://") || getDirectoryUri().startsWith("recent://") || getDirectoryUri().startsWith("favorite://") || getDirectoryUri().startsWith("search://")) return; //standardPaths not allow rename auto currentSelections = getSelections(); bool hasStandardPath = FileUtils::containsStandardPath(currentSelections); if (hasStandardPath) return; //delay edit action to avoid doubleClick or dragEvent qDebug()<<"slotRename"<stop(); setIndexWidget(m_last_index, nullptr); edit(m_last_index); m_editValid = false; } }); } void ListView::setProxy(DirectoryViewProxyIface *proxy) { } void ListView::resort() { m_proxy_model->sort(getSortType(), Qt::SortOrder(getSortOrder())); } void ListView::reportViewDirectoryChanged() { Q_EMIT m_proxy->viewDirectoryChanged(); } void ListView::adjustColumnsSize() { if (!model()) return; if (model()->columnCount() == 0) return; header()->resizeSections(QHeaderView::ResizeToContents); int rightPartsSize = 0; for (int column = 1; column < model()->columnCount(); column++) { int columnSize = header()->sectionSize(column); rightPartsSize += columnSize; } //set column 0 minimum width, fix header icon overlap with name issue if(columnWidth(0) < columnWidth(1)) setColumnWidth(0, columnWidth(1)); if (this->width() - rightPartsSize < BOTTOM_STATUS_MARGIN) { int size = width() - BOTTOM_STATUS_MARGIN; size /= header()->count() - 1; setColumnWidth(0, BOTTOM_STATUS_MARGIN); for (int column = 1; column < model()->columnCount(); column++) { setColumnWidth(column, size); } return; } header()->resizeSection(0, this->viewport()->width() - rightPartsSize); } void ListView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) { QTreeView::dataChanged(topLeft, bottomRight, roles); reUpdateScrollBar(); } DirectoryViewProxyIface *ListView::getProxy() { return m_proxy; } const QString ListView::getDirectoryUri() { if (!m_model) return nullptr; return m_model->getRootUri(); } void ListView::setDirectoryUri(const QString &uri) { m_current_uri = uri; } const QStringList ListView::getSelections() { QStringList uris; QString uri; QModelIndexList selections = selectedIndexes(); for (auto index : selections) { if (index.column() == 0) uris<indexFromUri(uri); if (index.isValid()) { QItemSelection selectionToBeMerged(index, index); selection.merge(selectionToBeMerged, QItemSelectionModel::Select); } } auto flags = QItemSelectionModel::Select|QItemSelectionModel::Rows; selectionModel()->select(selection, flags); } const QStringList ListView::getAllFileUris() { return m_proxy_model->getAllFileUris(); } QRect ListView::visualRect(const QModelIndex &index) const { auto rect = QTreeView::visualRect(index); //comment to fix rename state not show icon issue // if (index.column() == 0) { // rect.setX(0); // } return rect; } void ListView::open(const QStringList &uris, bool newWindow) { return; } void ListView::beginLocationChange() { m_editValid = false; m_last_index = QModelIndex(); //setModel(nullptr); m_model->setRootUri(m_current_uri); } void ListView::stopLocationChange() { m_model->cancelFindChildren(); } void ListView::closeView() { this->deleteLater(); } void ListView::invertSelections() { QItemSelectionModel *selectionModel = this->selectionModel(); const QItemSelection currentSelection = selectionModel->selection(); this->selectAll(); selectionModel->select(currentSelection, QItemSelectionModel::Deselect); } void ListView::scrollToSelection(const QString &uri) { auto index = m_proxy_model->indexFromUri(uri); QTreeView::scrollTo(index); reUpdateScrollBar(); } void ListView::setCutFiles(const QStringList &uris) { return; } int ListView::getSortType() { int type = m_proxy_model->sortColumn(); return type<0? 0: type; } void ListView::setSortType(int sortType) { //fix indicator not agree with actual sort order issue, link to bug#71475 header()->setSortIndicator(sortType, Qt::SortOrder(getSortOrder())); m_proxy_model->sort(sortType, Qt::SortOrder(getSortOrder())); } int ListView::getSortOrder() { return m_proxy_model->sortOrder(); } void ListView::setSortOrder(int sortOrder) { //fix indicator not agree with actual sort order issue, link to bug#71475 header()->setSortIndicator(getSortType(), Qt::SortOrder(sortOrder)); m_proxy_model->sort(getSortType(), Qt::SortOrder(sortOrder)); } void ListView::editUri(const QString &uri) { setState(QTreeView::NoState); auto origin = FileUtils::getOriginalUri(uri); setIndexWidget(m_proxy_model->indexFromUri(origin), nullptr); //注释该行以修复bug:#60474 // QTreeView::scrollTo(m_proxy_model->indexFromUri(origin)); edit(m_proxy_model->indexFromUri(origin)); } void ListView::editUris(const QStringList uris) { //FIXME: //implement batch rename. } void ListView::keyboardSearch(const QString &key) { // ensure current index is index in display name column if (currentIndex().column() != 0) { selectionModel()->setCurrentIndex(m_model->index(currentIndex().row(), 0, currentIndex().parent()), QItemSelectionModel::SelectCurrent); } // note: checking qtreeview.cpp we can find that the keyboard search only select rows // while selection mode is single selection. so we have a trick here for trigger that // action. setSelectionMode(QTreeView::SingleSelection); QAbstractItemView::keyboardSearch(key); setSelectionMode(QTreeView::ExtendedSelection); auto indexes = selectedIndexes(); if (!indexes.isEmpty()) { QTreeView::scrollTo(indexes.first(), QTreeView::PositionAtCenter); reUpdateScrollBar(); if (verticalScrollBar()->value() < viewport()->height()) { return; } verticalScrollBar()->setValue(qMin(verticalScrollBar()->value() + iconSize().height(), verticalScrollBar()->maximum())); } } //List View 2 ListView2::ListView2(QWidget *parent) : DirectoryViewWidget(parent) { auto layout = new QVBoxLayout(this); layout->setMargin(0); layout->setSpacing(0); m_view = new ListView(this); int defaultZoomLevel = GlobalSettings::getInstance()->getValue(DEFAULT_VIEW_ZOOM_LEVEL).toInt(); if (defaultZoomLevel >= minimumZoomLevel() && defaultZoomLevel <= maximumZoomLevel()) m_zoom_level = defaultZoomLevel; connect(m_view, &ListView::zoomLevelChangedRequest, this, &ListView2::zoomRequest); layout->addWidget(m_view); setLayout(layout); } ListView2::~ListView2() { m_model->setPositiveResponse(true); } void ListView2::bindModel(FileItemModel *model, FileItemProxyFilterSortModel *proxyModel) { disconnect(m_model); disconnect(m_proxy_model); m_model = model; m_proxy_model = proxyModel; //m_model->setPositiveResponse(false); m_view->bindModel(model, proxyModel); connect(m_model, &FileItemModel::selectRequest, this, &DirectoryViewWidget::updateWindowSelectionRequest); connect(model, &FileItemModel::findChildrenFinished, this, &DirectoryViewWidget::viewDirectoryChanged); //connect(m_model, &FileItemModel::updated, m_view, &ListView::resort); connect(m_view->selectionModel(), &QItemSelectionModel::selectionChanged, this, &DirectoryViewWidget::viewSelectionChanged); connect(m_view, &ListView::activated, this, [=](const QModelIndex &index) { //when selections is more than 1, let mainwindow to process if (getSelections().count() != 1) return; auto uri = getSelections().first(); Q_EMIT this->viewDoubleClicked(uri); }); //FIXME: how about multi-selection? //menu connect(m_view, &ListView::customContextMenuRequested, this, [=](const QPoint &pos) { qDebug()<<"menu request"; if (!m_view->indexAt(pos).isValid()) { m_view->clearSelection(); //m_view->clearFocus(); } auto index = m_view->indexAt(pos); auto selectedIndexes = m_view->selectionModel()->selection().indexes(); auto visualRect = m_view->visualRect(index); auto sizeHint = m_view->itemDelegate()->sizeHint(m_view->viewOptions(), index); auto validRect = QRect(visualRect.topLeft(), sizeHint); if (!selectedIndexes.contains(index)) { if (!validRect.contains(pos)) { m_view->clearSelection(); //m_view->clearFocus(); } else { auto flags = QItemSelectionModel::Select|QItemSelectionModel::Rows; m_view->clearSelection(); //m_view->clearFocus(); m_view->selectionModel()->select(m_view->indexAt(pos), flags); } } //NOTE: we have to ensure that we have cleared the //selection if menu request at blank pos. QTimer::singleShot(1, [=]() { Q_EMIT this->menuRequest(QCursor::pos()); }); }); connect(m_proxy_model, &FileItemProxyFilterSortModel::layoutChanged, this, [=]() { Q_EMIT this->sortOrderChanged(Qt::SortOrder(getSortOrder())); }); connect(m_proxy_model, &FileItemProxyFilterSortModel::layoutChanged, this, [=]() { Q_EMIT this->sortTypeChanged(getSortType()); }); connect(m_model, &FileItemModel::findChildrenFinished, this, [=]() { if (m_need_resize_header) { m_view->adjustColumnsSize(); } m_need_resize_header = false; }); } void ListView2::repaintView() { m_view->update(); m_view->viewport()->update(); } void ListView2::setCurrentZoomLevel(int zoomLevel) { int base = 16; int adjusted = base + zoomLevel; m_view->setIconSize(QSize(adjusted, adjusted)); m_zoom_level = zoomLevel; } void ListView2::clearIndexWidget() { for (auto index : m_proxy_model->getAllFileIndexes()) { m_view->setIndexWidget(index, nullptr); m_view->closePersistentEditor(index); } } peony/libpeony-qt/controls/directory-view/view/view.pri0000644000175000017500000000013014205101223022332 0ustar fengfengINCLUDEPATH += $$PWD include(icon-view/icon-view.pri) include(list-view/list-view.pri) peony/libpeony-qt/controls/directory-view/view/icon-view/0000755000175000017500000000000014205115226022562 5ustar fengfengpeony/libpeony-qt/controls/directory-view/view/icon-view/icon-view-style.cpp0000644000175000017500000000446014205115226026330 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "icon-view-style.h" #include #include #include using namespace Peony; using namespace Peony::DirectoryView; static IconViewStyle *global_instance = nullptr; IconViewStyle::IconViewStyle(QStyle *style) : QProxyStyle(style) { //qDebug()<<"icon view style"; } IconViewStyle *IconViewStyle::getStyle() { if (!global_instance) global_instance = new IconViewStyle; return global_instance; } void IconViewStyle::release() { if (global_instance) global_instance->deleteLater(); } void IconViewStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { if (element == PE_Frame) { return; } return QProxyStyle::drawPrimitive(element, option, painter, widget); } void IconViewStyle::drawControl(QStyle::ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { QProxyStyle::drawControl(element, option, painter, widget); } void IconViewStyle::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, const QPixmap &pixmap) const { //qDebug()<<"drawItemPixmap"; QProxyStyle::drawItemPixmap(painter, rect, alignment, pixmap); } void IconViewStyle::drawItemText(QPainter *painter, const QRect &rect, int flags, const QPalette &pal, bool enabled, const QString &text, QPalette::ColorRole textRole) const { //qDebug()<<"drawItemText"; QProxyStyle::drawItemText(painter, rect, flags, pal, enabled, text, textRole); } peony/libpeony-qt/controls/directory-view/view/icon-view/icon-view-style.h0000644000175000017500000000472314205101223025767 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef ICONVIEWSTYLE_H #define ICONVIEWSTYLE_H #include #include namespace Peony { namespace DirectoryView { /*! * \brief The IconViewStyle class * \details * This class is aim to provide a custom style for IconView's items. * In Qt5, item view draw its items from styled delegate by default, * even though we can implement a item's painting in delegate's paint(). * I think it is better to follow Qt's desgin. * I mainly use this class control the internal layout of a item. It will * combine with delegate's paint() method. */ class PEONYCORESHARED_EXPORT IconViewStyle : public QProxyStyle { Q_OBJECT public: static IconViewStyle *getStyle(); void release(); void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const override; void drawControl(QStyle::ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const override; void drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, const QPixmap &pixmap) const override; void drawItemText(QPainter *painter, const QRect &rect, int flags, const QPalette &pal, bool enabled, const QString &text, QPalette::ColorRole textRole = QPalette::NoRole) const override; private: explicit IconViewStyle(QStyle *style = nullptr); ~IconViewStyle() override {} }; } } #endif // ICONVIEWSTYLE_H peony/libpeony-qt/controls/directory-view/view/icon-view/icon-view.h0000644000175000017500000001675414205115226024650 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #ifndef ICONVIEW_H #define ICONVIEW_H #include "peony-core_global.h" #include "directory-view-plugin-iface.h" #include "file-item-model.h" #include "file-item-proxy-filter-sort-model.h" #include "directory-view-widget.h" #include #include namespace Peony { namespace DirectoryView { class IconViewDelegate; class PEONYCORESHARED_EXPORT IconView : public QListView, public DirectoryViewIface { friend class IconViewDelegate; friend class IconViewIndexWidget; friend class IconView2; Q_OBJECT public: /*! * \brief IconView * \param parent * \deprecated * We should not create a proxy in a view itself. Proxy should be created by tabpage * or FMWindow. */ explicit IconView(QWidget *parent = nullptr); explicit IconView(DirectoryViewProxyIface *proxy, QWidget *parent = nullptr); ~IconView() override; bool isDraggingState() { return this->state() == QListView::DraggingState || this->state() == QListView::DragSelectingState; } const QString viewId() override { return tr("Icon View"); } void bindModel(FileItemModel *sourceModel, FileItemProxyFilterSortModel *proxyModel) override; void setProxy(DirectoryViewProxyIface *proxy) override; /*! * \brief setUsePeonyQtDirectoryMenu * \param use * \deprecated */ void setUsePeonyQtDirectoryMenu(bool use) { m_use_peony_qt_directory_menu = use; } /*! * \brief getProxy * \return * \deprecated */ DirectoryViewProxyIface *getProxy() override; //location const QString getDirectoryUri() override; //selections const QStringList getSelections() override; //children const QStringList getAllFileUris() override; QRect visualRect(const QModelIndex &index) const override; bool getDelegateEditFlag(); Q_SIGNALS: void zoomLevelChangedRequest(bool zoomIn); public Q_SLOTS: //location void open(const QStringList &uris, bool newWindow) override; void setDirectoryUri(const QString &uri) override; void beginLocationChange() override; void stopLocationChange() override; void closeView() override; //selections void setSelections(const QStringList &uris) override; void invertSelections() override; void scrollToSelection(const QString &uri) override; //clipboard void setCutFiles(const QStringList &uris) override; int getSortType() override; void setSortType(int sortType) override; int getSortOrder() override; void setSortOrder(int sortOrder) override; void editUri(const QString &uri) override; void editUris(const QStringList uris) override; void selectAll() override; void resort(); void reportViewDirectoryChanged(); void clearIndexWidget(); protected: /*! * \brief changeZoomLevel * \deprecated */ void changeZoomLevel(); void dragEnterEvent(QDragEnterEvent *e) override; void dragMoveEvent(QDragMoveEvent *e) override; void dropEvent(QDropEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void mouseDoubleClickEvent(QMouseEvent *event) override; void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; void wheelEvent(QWheelEvent *e) override; void updateGeometries() override; void focusInEvent(QFocusEvent *e) override; bool getIgnore_mouse_move_event() const; void setIgnore_mouse_move_event(bool ignore_mouse_move_event); private Q_SLOTS: void slotRename(); private: QTimer m_repaint_timer; bool m_editValid; bool m_ctrl_key_pressed; QTimer* m_renameTimer; QModelIndex m_last_index; DirectoryViewProxyIface *m_proxy = nullptr; FileItemModel *m_model = nullptr; FileItemProxyFilterSortModel *m_sort_filter_proxy_model = nullptr; QString m_current_uri = nullptr; /*! * \brief m_use_peony_qt_directory_menu * \deprecated */ bool m_use_peony_qt_directory_menu = false; const int BOTTOM_STATUS_MARGIN = 36; bool m_ignore_mouse_move_event = false; bool m_delegate_editing = false; bool m_allow_set_index_widget = true; bool m_slider_bar_draging = false; }; //IconView2 class IconView2 : public DirectoryViewWidget { Q_OBJECT //internal plugin public: explicit IconView2(QWidget *parent = nullptr); ~IconView2(); const QString viewId() { return "Icon View"; } //location const QString getDirectoryUri() { return m_view->getDirectoryUri(); } //selections const QStringList getSelections() { return m_view->getSelections(); } //children const QStringList getAllFileUris() { return m_view->getAllFileUris(); } int getSortType() { return m_view->getSortType(); } Qt::SortOrder getSortOrder() { return Qt::SortOrder(m_view->getSortOrder()); } int currentZoomLevel() { return m_zoom_level; } int minimumZoomLevel() { return 21; } int maximumZoomLevel() { return 100; } bool supportZoom() { return true; } public Q_SLOTS: void bindModel(FileItemModel *model, FileItemProxyFilterSortModel *proxyModel); //location //void open(const QStringList &uris, bool newWindow); void setDirectoryUri(const QString &uri) { m_view->setDirectoryUri(uri); } void beginLocationChange() { m_view->beginLocationChange(); } void stopLocationChange() { m_view->stopLocationChange(); } void closeDirectoryView() { m_view->closeView(); } //selections void setSelections(const QStringList &uris) { m_view->setSelections(uris); } void invertSelections() { m_view->invertSelections(); } void scrollToSelection(const QString &uri) { m_view->scrollToSelection(uri); } //clipboard //cut items should be drawn differently. void setCutFiles(const QStringList &uris) { m_view->setCutFiles(uris); } void setSortType(int sortType) { m_view->setSortType(sortType); } void setSortOrder(int sortOrder) { m_view->setSortOrder(sortOrder); } void editUri(const QString &uri) { m_view->editUri(uri); } void editUris(const QStringList uris) { m_view->editUris(uris); } void repaintView(); void setCurrentZoomLevel(int zoomLevel); void clearIndexWidget(); private: IconView *m_view = nullptr; FileItemModel *m_model = nullptr; FileItemProxyFilterSortModel *m_proxy_model = nullptr; int m_zoom_level = 25; }; } } #endif // ICONVIEW_H peony/libpeony-qt/controls/directory-view/view/icon-view/icon-view.pri0000644000175000017500000000023514205101223025166 0ustar fengfengINCLUDEPATH += $$PWD HEADERS += \ $$PWD/icon-view.h \ $$PWD/icon-view-style.h SOURCES += \ $$PWD/icon-view.cpp \ $$PWD/icon-view-style.cpp peony/libpeony-qt/controls/directory-view/view/icon-view/icon-view.cpp0000644000175000017500000005520714205115226025177 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #include "icon-view.h" #include "file-item.h" #include "icon-view-delegate.h" #include "icon-view-style.h" #include "directory-view-menu.h" #include "file-info.h" #include "file-utils.h" #include "global-settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Peony; using namespace Peony::DirectoryView; IconView::IconView(QWidget *parent) : QListView(parent) { setAttribute(Qt::WA_TranslucentBackground); viewport()->setAttribute(Qt::WA_TranslucentBackground); setAutoScroll(true); setAutoScrollMargin(100); setStyle(IconViewStyle::getStyle()); //FIXME: do not create proxy in view itself. IconViewDelegate *delegate = new IconViewDelegate(this); setItemDelegate(delegate); connect(delegate, &IconViewDelegate::isEditing, this, [=](const bool &editing) { m_delegate_editing = editing; }); //fix long file name not fully painted issue when drag sliderbar QScrollBar *verticalBar = verticalScrollBar(); connect(verticalBar, &QScrollBar::sliderPressed, [=](){ m_slider_bar_draging = true; }); connect(verticalBar, &QScrollBar::sliderReleased, [=](){ m_slider_bar_draging = false; }); connect(verticalBar, &QScrollBar::valueChanged, [=](){ if (m_slider_bar_draging) viewport()->update(viewport()->rect()); }); setSelectionMode(QListView::ExtendedSelection); setEditTriggers(QListView::NoEditTriggers); setViewMode(QListView::IconMode); setResizeMode(QListView::Adjust); setMovement(QListView::Snap); //setWordWrap(true); setContextMenuPolicy(Qt::CustomContextMenu); setGridSize(QSize(115, 135)); setIconSize(QSize(64, 64)); m_renameTimer = new QTimer(this); m_renameTimer->setInterval(3000); m_renameTimer->setSingleShot(true); m_editValid = false; setMouseTracking(true);//追踪鼠标 } IconView::~IconView() { } DirectoryViewProxyIface *IconView::getProxy() { return m_proxy; } //selection //FIXME: implement the selection functions. void IconView::setSelections(const QStringList &uris) { clearSelection(); QItemSelection selection; for (auto uri: uris) { const QModelIndex index = m_sort_filter_proxy_model->indexFromUri(uri); if (index.isValid()) { QItemSelection selectionToBeMerged(index, index); selection.merge(selectionToBeMerged, QItemSelectionModel::Select); } } selectionModel()->select(selection, QItemSelectionModel::Select); } const QStringList IconView::getSelections() { QStringList uris; QModelIndexList selections = selectedIndexes(); for (auto index : selections) { auto item = m_sort_filter_proxy_model->itemFromIndex(index); uris<uri(); } uris.removeDuplicates(); return uris; } void IconView::invertSelections() { QItemSelectionModel *selectionModel = this->selectionModel(); const QItemSelection currentSelection = selectionModel->selection(); this->selectAll(); selectionModel->select(currentSelection, QItemSelectionModel::Deselect); } void IconView::scrollToSelection(const QString &uri) { auto index = m_sort_filter_proxy_model->indexFromUri(uri); scrollTo(index); } //clipboard void IconView::setCutFiles(const QStringList &uris) { //let delegate and model know how to deal with cut files. } //location //FIXME: implement location functions. void IconView::setDirectoryUri(const QString &uri) { m_current_uri = uri; } const QString IconView::getDirectoryUri() { return m_model->getRootUri(); } void IconView::beginLocationChange() { m_editValid = false; m_model->setRootUri(m_current_uri); } void IconView::stopLocationChange() { m_model->cancelFindChildren(); } //other void IconView::open(const QStringList &uris, bool newWindow) { } void IconView::closeView() { this->deleteLater(); } void IconView::dragEnterEvent(QDragEnterEvent *e) { m_editValid = 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; qDebug()<<"dragEnterEvent()" <mimeData()->hasUrls()) { e->setDropAction(action); e->accept(); } } void IconView::dragMoveEvent(QDragMoveEvent *e) { if (e->keyboardModifiers() & Qt::ControlModifier) m_ctrl_key_pressed = true; else m_ctrl_key_pressed = false; //fix can not drag in the second time issue if (this->isDraggingState()) { if (m_allow_set_index_widget) { m_allow_set_index_widget = false; for (auto index : selectedIndexes()) { setIndexWidget(index, nullptr); } } } auto action = m_ctrl_key_pressed ? Qt::CopyAction : Qt::MoveAction; //qDebug()<<"dragMoveEvent()" <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 (this == e->source()) { return QListView::dragMoveEvent(e); } e->setDropAction(action); e->accept(); } void IconView::dropEvent(QDropEvent *e) { m_last_index = QModelIndex(); //m_edit_trigger_timer.stop(); 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; e->setDropAction(action); if (e->keyboardModifiers() & Qt::ShiftModifier) { action = Qt::TargetMoveAction; } auto proxy_index = indexAt(e->pos()); auto index = m_sort_filter_proxy_model->mapToSource(proxy_index); qDebug()<<"dropEvent" <pos()).isValid(); //move in current path, do nothing if (e->source() == this) { if (indexAt(e->pos()).isValid()) { auto uri = m_sort_filter_proxy_model->itemFromIndex(proxy_index)->uri(); if(!e->mimeData()->urls().contains(uri)) m_model->dropMimeData(e->mimeData(), action, 0, 0, index); } else { if (m_ctrl_key_pressed) { m_model->dropMimeData(e->mimeData(), Qt::CopyAction, 0, 0, QModelIndex()); } } return; } m_model->dropMimeData(e->mimeData(), action, 0, 0, index); } void IconView::mouseMoveEvent(QMouseEvent *e) { QModelIndex itemIndex = indexAt(e->pos()); if (!itemIndex.isValid()) { if (QToolTip::isVisible()) { QToolTip::hideText(); } } if (m_ignore_mouse_move_event) { return; } QListView::mouseMoveEvent(e); } void IconView::mousePressEvent(QMouseEvent *e) { m_allow_set_index_widget = true; qDebug()<<"moursePressEvent"; m_editValid = true; QListView::mousePressEvent(e); if (e->button() != Qt::LeftButton) { return; } //m_renameTimer if(!m_renameTimer->isActive()) { m_renameTimer->start(); m_editValid = false; } else { //if remain time is between[0.75, 3000],then trigger rename event; if(m_renameTimer->remainingTime()>=0 && m_renameTimer->remainingTime() <= 3000 - qApp->styleHints()->mouseDoubleClickInterval() && indexAt(e->pos()) == m_last_index && m_last_index.isValid() && m_editValid == true) { slotRename(); } else { m_editValid = false; } } } void IconView::mouseReleaseEvent(QMouseEvent *e) { QListView::mouseReleaseEvent(e); if (e->button() != Qt::LeftButton) { return; } } void IconView::mouseDoubleClickEvent(QMouseEvent *event) { m_editValid = false; QListView::mouseDoubleClickEvent(event); } void IconView::paintEvent(QPaintEvent *e) { QPainter p(this->viewport()); p.fillRect(this->geometry(), this->palette().base()); if (m_repaint_timer.isActive()) { m_repaint_timer.stop(); QTimer::singleShot(100, this, [this]() { this->repaint(); }); } QListView::paintEvent(e); } void IconView::resizeEvent(QResizeEvent *e) { //FIXME: first resize is disfluency. //but I have to reset the index widget in view's resize. QListView::resizeEvent(e); setIndexWidget(m_last_index, nullptr); } void IconView::wheelEvent(QWheelEvent *e) { if (e->modifiers() & Qt::ControlModifier) { if (e->delta() > 0) { zoomLevelChangedRequest(true); } else { zoomLevelChangedRequest(false); } return; } QListView::wheelEvent(e); if (e->buttons() == Qt::LeftButton) this->viewport()->update(); } void IconView::updateGeometries() { #if QT_VERSION_CHECK(5, 15, 0) //try fix #55341 int verticalValue = verticalOffset(); QListView::updateGeometries(); if (!model() || model()->columnCount() == 0 || model()->rowCount() == 0) { return; } int itemCount = model()->rowCount(); QRegion itemRegion; for (int row = 0; row < itemCount; row++) { auto index = model()->index(row, 0); itemRegion += visualRect(index); } if (itemRegion.boundingRect().bottom() + gridSize().height() < viewport()->height()) { verticalScrollBar()->setRange(0, 0); } else { int vertiacalMax = verticalScrollBar()->maximum(); verticalScrollBar()->setMaximum(vertiacalMax + gridSize().height()); verticalScrollBar()->setValue(verticalValue); } #else horizontalScrollBar()->setRange(0, 0); if (!model()) { verticalScrollBar()->setRange(0, 0); return; } if (model()->columnCount() == 0 || model()->rowCount() == 0) { verticalScrollBar()->setRange(0, 0); return; } int itemCount = model()->rowCount(); QRegion itemRegion; for (int row = 0; row < itemCount; row++) { auto index = model()->index(row, 0); itemRegion += visualRect(index); } if (itemRegion.boundingRect().bottom() + gridSize().height() < viewport()->height()) { verticalScrollBar()->setRange(0, 0); } else { verticalScrollBar()->setSingleStep(gridSize().height()/2); verticalScrollBar()->setPageStep(viewport()->height()); verticalScrollBar()->setRange(0, itemRegion.boundingRect().bottom() - viewport()->height() + gridSize().height()); } #endif } void IconView::focusInEvent(QFocusEvent *e) { QListView::focusInEvent(e); if (e->reason() == Qt::TabFocusReason) { if (selectedIndexes().isEmpty()) { selectionModel()->select(model()->index(0, 0), QItemSelectionModel::SelectCurrent|QItemSelectionModel::Rows); } else { scrollTo(selectedIndexes().first(), QListView::PositionAtCenter); //auto selections = selectedIndexes(); clearSelection(); //added for tab key to focus button issue //comment to fix crash bug#68788 // QTimer::singleShot(100, this, [=](){ // for (auto index : selections) { // selectionModel()->select(index, QItemSelectionModel::Select); // } // }); } } } void IconView::slotRename() { //special path like trash path not allow rename if (getDirectoryUri().startsWith("trash://") || getDirectoryUri().startsWith("recent://") || getDirectoryUri().startsWith("favorite://") || getDirectoryUri().startsWith("search://")) return; //standardPaths not allow rename auto currentSelections = getSelections(); bool hasStandardPath = FileUtils::containsStandardPath(currentSelections); if (hasStandardPath) return; //delay edit action to avoid doubleClick or dragEvent qDebug()<<"slotRename"<stop(); setIndexWidget(m_last_index, nullptr); edit(m_last_index); m_editValid = false; } }); } bool IconView::getIgnore_mouse_move_event() const { return m_ignore_mouse_move_event; } void IconView::setIgnore_mouse_move_event(bool ignore_mouse_move_event) { m_ignore_mouse_move_event = ignore_mouse_move_event; } void IconView::bindModel(FileItemModel *sourceModel, FileItemProxyFilterSortModel *proxyModel) { m_model = sourceModel; m_sort_filter_proxy_model = proxyModel; setModel(m_sort_filter_proxy_model); //edit trigger connect(this->selectionModel(), &QItemSelectionModel::selectionChanged, [=](const QItemSelection &selection, const QItemSelection &deselection) { qDebug()<<"selection changed"; auto currentSelections = selection.indexes(); for (auto index : deselection.indexes()) { this->setIndexWidget(index, nullptr); } if (state() == QListView::DragSelectingState) m_allow_set_index_widget = false; //Q_EMIT m_proxy->viewSelectionChanged(); if (currentSelections.count() == 1) { qDebug()<<"m_last_index "<<(m_last_index == currentSelections.first())<resetEditTriggerTimer(); } else { m_last_index = QModelIndex(); m_editValid = false; } qDebug()<<"selection changed2"<viewDoubleClicked(uri); }); //menu // connect(this, &IconView::customContextMenuRequested, [=](const QPoint &pos){ // if (!indexAt(pos).isValid()) // this->clearSelection(); // //NOTE: we have to ensure that we have cleared the // //selection if menu request at blank pos. // QTimer::singleShot(1, [=](){ // Q_EMIT this->getProxy()->menuRequest(QCursor::pos()); // }); // }); } // NOTE: When icon view was resorted, // index widget would deviated from its normal position by somehow. // So, do not set any index widget when the resorting. void IconView::resort() { //fix uncompress selected file will cover file before it issue clearIndexWidget(); if (m_last_index.isValid()) { this->setIndexWidget(m_last_index, nullptr); } if (m_sort_filter_proxy_model) m_sort_filter_proxy_model->sort(getSortType(), Qt::SortOrder(getSortOrder())); } void IconView::reportViewDirectoryChanged() { if (m_proxy) Q_EMIT m_proxy->viewDirectoryChanged(); } QRect IconView::visualRect(const QModelIndex &index) const { auto rect = QListView::visualRect(index); rect.setX(rect.x() + 10); rect.setY(rect.y() + 15); auto size = itemDelegate()->sizeHint(QStyleOptionViewItem(), index); rect.setSize(size); return rect; } bool IconView::getDelegateEditFlag() { return m_delegate_editing; } int IconView::getSortType() { int type = m_sort_filter_proxy_model->sortColumn(); return type<0? 0: type; } void IconView::setSortType(int sortType) { m_sort_filter_proxy_model->sort(sortType, Qt::SortOrder(getSortOrder())); } int IconView::getSortOrder() { return m_sort_filter_proxy_model->sortOrder(); } void IconView::setSortOrder(int sortOrder) { m_sort_filter_proxy_model->sort(getSortType(), Qt::SortOrder(sortOrder)); } const QStringList IconView::getAllFileUris() { return m_sort_filter_proxy_model->getAllFileUris(); } void IconView::editUri(const QString &uri) { setState(QListView::NoState); auto origin = FileUtils::getOriginalUri(uri); setIndexWidget(m_sort_filter_proxy_model->indexFromUri(origin), nullptr); qDebug() <<"editUri:" <indexFromUri(origin)); edit(m_sort_filter_proxy_model->indexFromUri(origin)); // if (! m_delegate_editing) // edit(m_sort_filter_proxy_model->indexFromUri(origin)); } void IconView::editUris(const QStringList uris) { //FIXME: //implement batch rename. } void IconView::selectAll() { // fix: #62397 // for (int i = 0; i < model()->rowCount(); i++) { // selectionModel()->select(model()->index(i, 0), QItemSelectionModel::Select); // } // optimize selectAll(). do not trigger selection changed signal to many times. QItemSelection selection(model()->index(0, 0), model()->index(model()->rowCount() - 1, 0)); selectionModel()->select(selection, QItemSelectionModel::Select); } void IconView::clearIndexWidget() { for (int i = 0; i < m_sort_filter_proxy_model->rowCount(); i++) { auto index = m_sort_filter_proxy_model->index(i, 0); setIndexWidget(index, nullptr); closePersistentEditor(index); } } //Icon View 2 IconView2::IconView2(QWidget *parent) : DirectoryViewWidget(parent) { auto layout = new QVBoxLayout(this); layout->setMargin(0); layout->setSpacing(0); m_view = new IconView(this); int defaultZoomLevel = GlobalSettings::getInstance()->getValue(DEFAULT_VIEW_ZOOM_LEVEL).toInt(); if (defaultZoomLevel >= minimumZoomLevel() && defaultZoomLevel <= maximumZoomLevel()) m_zoom_level = defaultZoomLevel; connect(m_view, &IconView::zoomLevelChangedRequest, this, &IconView2::zoomRequest); layout->addWidget(m_view); setLayout(layout); } IconView2::~IconView2() { } void IconView2::bindModel(FileItemModel *model, FileItemProxyFilterSortModel *proxyModel) { disconnect(m_model); disconnect(m_proxy_model); m_model = model; m_proxy_model = proxyModel; m_view->bindModel(model, proxyModel); connect(m_model, &FileItemModel::selectRequest, this, &DirectoryViewWidget::updateWindowSelectionRequest); connect(model, &FileItemModel::findChildrenFinished, this, &DirectoryViewWidget::viewDirectoryChanged); //connect(m_model, &FileItemModel::dataChanged, m_view, &IconView::clearIndexWidget); //connect(m_model, &FileItemModel::updated, m_view, &IconView::resort); connect(m_view->selectionModel(), &QItemSelectionModel::selectionChanged, this, &DirectoryViewWidget::viewSelectionChanged); connect(m_view, &IconView::activated, this, [=](const QModelIndex &index) { //when selections is more than 1, let mainwindow to process if (getSelections().count() != 1) return; auto uri = getSelections().first(); Q_EMIT this->viewDoubleClicked(uri); }); connect(m_view, &IconView::customContextMenuRequested, this, [=](const QPoint &pos) { // we should clear the dirty rubber band due to call context menu. bool isDragSelecting = m_view->isDraggingState(); if (isDragSelecting) { // send a fake mouse release event for clear the rubber band. // note that we should pass mouse move event durring delaying requesting // directory view menu, otherwize both dnd and menu action will be triggered. m_view->setIgnore_mouse_move_event(true); QMouseEvent fakeEvent(QMouseEvent::MouseButtonRelease, pos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); qApp->sendEvent(m_view, &fakeEvent); m_view->viewport()->repaint(); } if (!m_view->indexAt(pos).isValid()) m_view->clearSelection(); //NOTE: we have to ensure that we have cleared the //selection if menu request at blank pos. QTimer::singleShot(isDragSelecting? 300: 1, this, [=]() { m_view->setIgnore_mouse_move_event(false); Q_EMIT this->menuRequest(QCursor::pos()); }); }); connect(m_proxy_model, &FileItemProxyFilterSortModel::layoutChanged, this, [=]() { Q_EMIT this->sortOrderChanged(Qt::SortOrder(getSortOrder())); }); connect(m_proxy_model, &FileItemProxyFilterSortModel::layoutChanged, this, [=]() { Q_EMIT this->sortTypeChanged(getSortType()); }); } void IconView2::repaintView() { m_view->update(); m_view->viewport()->update(); } void IconView2::setCurrentZoomLevel(int zoomLevel) { if (zoomLevel <= maximumZoomLevel() && zoomLevel >= minimumZoomLevel()) { m_zoom_level = zoomLevel; //FIXME: implement zoom int base = 64 - 25; //50 int adjusted = base + zoomLevel; m_view->setIconSize(QSize(adjusted, adjusted)); m_view->setGridSize(m_view->itemDelegate()->sizeHint(QStyleOptionViewItem(), QModelIndex()) + QSize(20, 20)); } } void IconView2::clearIndexWidget() { for (auto index : m_proxy_model->getAllFileIndexes()) { m_view->closePersistentEditor(index); m_view->setIndexWidget(index, nullptr); } } peony/libpeony-qt/controls/directory-view/directory-view-container.cpp0000644000175000017500000003404314205115226025354 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * Authors: Meihong He * */ #include "directory-view-container.h" #include "directory-view-plugin-iface.h" #include "directory-view-plugin-iface2.h" #include "directory-view-widget.h" #include "directory-view-factory-manager.h" #include "file-utils.h" #include "global-settings.h" #include "file-label-model.h" #include "directory-view-factory-manager.h" #include "file-item-proxy-filter-sort-model.h" #include "file-info.h" #include #include #include using namespace Peony; DirectoryViewContainer::DirectoryViewContainer(QWidget *parent) : QWidget(parent) { m_model = new FileItemModel(this); m_proxy_model = new FileItemProxyFilterSortModel(this); m_proxy_model->setSourceModel(m_model); //m_proxy = new DirectoryView::StandardViewProxy; setContentsMargins(0, 0, 0, 0); m_layout = new QVBoxLayout(this); m_layout->setMargin(0); m_layout->setContentsMargins(0, 0, 0, 0); m_layout->setSpacing(0); setLayout(m_layout); // connect(m_proxy, &DirectoryViewProxyIface::viewDirectoryChanged, // this, &DirectoryViewContainer::directoryChanged); // connect(m_proxy, &DirectoryViewProxyIface::viewSelectionChanged, // this, &DirectoryViewContainer::selectionChanged); // connect(m_proxy, &DirectoryViewProxyIface::menuRequest, // this, &DirectoryViewContainer::menuRequest); connect(m_model, &FileItemModel::changePathRequest, this, &DirectoryViewContainer::updateWindowLocationRequest); connect(FileLabelModel::getGlobalModel(), &FileLabelModel::dataChanged, this, [=](){ refresh(); }); 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:" <viewId() == "List View" && (key == "date" || key == "hoursystem")) refresh(); }); } } DirectoryViewContainer::~DirectoryViewContainer() { // m_proxy->closeProxy(); // if (m_proxy->getView()) // m_proxy->getView()->closeView(); } const QStringList DirectoryViewContainer::getBackList() { QStringList l; for (auto uri : m_back_list) { l<getDirectoryUri()).isNull(); } void DirectoryViewContainer::cdUp() { if (!canCdUp()) return; auto uri = FileUtils::getParentUri(m_view->getDirectoryUri()); if (uri.isNull()) return; Q_EMIT updateWindowLocationRequest(uri, true); } void DirectoryViewContainer::setSortFilter(int FileTypeIndex, int FileMTimeIndex, int FileSizeIndex) { qDebug()<<"setSortFilter:"<setFilterConditions(FileTypeIndex, FileMTimeIndex, FileSizeIndex); } void DirectoryViewContainer::setFilterLabelConditions(QString name) { m_proxy_model->setFilterLabelConditions(name); } void DirectoryViewContainer::setShowHidden(bool showHidden) { m_proxy_model->setShowHidden(showHidden); } void DirectoryViewContainer::setUseDefaultNameSortOrder(bool use) { m_proxy_model->setUseDefaultNameSortOrder(use); } void DirectoryViewContainer::setSortFolderFirst(bool folderFirst) { m_proxy_model->setFolderFirst(folderFirst); } void DirectoryViewContainer::addFilterCondition(int option, int classify, bool updateNow) { m_proxy_model->addFilterCondition(option, classify, updateNow); } void DirectoryViewContainer::addFileNameFilter(QString key, bool updateNow) { m_proxy_model->addFileNameFilter(key, updateNow); } void DirectoryViewContainer::removeFilterCondition(int option, int classify, bool updateNow) { m_proxy_model->removeFilterCondition(option, classify, updateNow); } void DirectoryViewContainer::clearConditions() { m_proxy_model->clearConditions(); } void DirectoryViewContainer::updateFilter() { m_proxy_model->update(); } void DirectoryViewContainer::goToUri(const QString &uri, bool addHistory, bool forceUpdate) { int zoomLevel = -1; if (m_view) zoomLevel = m_view->currentZoomLevel(); if (forceUpdate) goto update; if (uri.isNull()) return; if (getCurrentUri() == uri) return; update: if (addHistory) { m_forward_list.clear(); //qDebug() << "getCurrentUri():" <getDefaultViewId(zoomLevel, uri); switchViewType(viewId); //update status bar zoom level updateStatusBarSliderStateRequest(); if (zoomLevel < 0) zoomLevel = getView()->currentZoomLevel(); setZoomLevelRequest(zoomLevel); //qDebug() << "setZoomLevelRequest:" <setCurrentZoomLevel(zoomLevel); m_current_uri = uri; //special uri process if (m_current_uri.endsWith("/.")) m_current_uri = m_current_uri.left(m_current_uri.length()-2); if (m_current_uri.endsWith("/..")) m_current_uri = m_current_uri.left(m_current_uri.length()-3); if (m_view) { m_view->setDirectoryUri(m_current_uri); m_view->beginLocationChange(); //m_active_view_prxoy->setDirectoryUri(uri); } } void DirectoryViewContainer::switchViewType(const QString &viewId) { /* if (!m_proxy) return; if (m_proxy->getView()) { if (viewId == m_proxy->getView()->viewId()) return; } */ if (getView()) { if (getView()->viewId() == viewId) return; } auto viewManager = DirectoryViewFactoryManager2::getInstance(); auto factory = viewManager->getFactory(viewId); if (!factory) return; auto settings = GlobalSettings::getInstance(); auto sortType = settings->isExist(SORT_COLUMN)? settings->getValue(SORT_COLUMN).toInt() : 0; auto sortOrder = settings->isExist(SORT_ORDER)? settings->getValue(SORT_ORDER).toInt() : 0; auto oldView = m_view; QStringList selection; if (oldView) { sortType = oldView->getSortType(); sortOrder = oldView->getSortOrder(); selection = oldView->getSelections(); m_layout->removeWidget(dynamic_cast(oldView)); oldView->deleteLater(); } auto view = factory->create(); m_view = view; view->setParent(this); //connect the view's signal. view->bindModel(m_model, m_proxy_model); //view->setProxy(m_proxy); //fix go to root path issue after refresh view->setDirectoryUri(getCurrentUri()); view->setSortType(sortType); view->setSortOrder(sortOrder); connect(m_view, &DirectoryViewWidget::menuRequest, this, &DirectoryViewContainer::menuRequest); connect(m_view, &DirectoryViewWidget::viewDirectoryChanged, this, [=](){ if (DirectoryViewFactoryManager2::getInstance()->internalViews().contains(m_view->viewId())) { auto dirInfo = FileInfo::fromUri(m_current_uri); if (dirInfo.get()->isEmptyInfo() && !dirInfo.get()->uri().startsWith("search://")) { goBack(); if (!m_forward_list.isEmpty()) m_forward_list.takeFirst(); } else { Q_EMIT this->directoryChanged(); } } else { Q_EMIT this->directoryChanged(); } }); connect(m_view, &DirectoryViewWidget::viewDoubleClicked, this, &DirectoryViewContainer::viewDoubleClicked); connect(m_view, &DirectoryViewWidget::viewDoubleClicked, this, &DirectoryViewContainer::onViewDoubleClicked); connect(m_view, &DirectoryViewWidget::viewSelectionChanged, this, &DirectoryViewContainer::selectionChanged); connect(m_view, &DirectoryViewWidget::zoomRequest, this, &DirectoryViewContainer::zoomRequest); connect(m_view, &DirectoryViewWidget::updateWindowSelectionRequest, this, &DirectoryViewContainer::updateWindowSelectionRequest); //similar to double clicked, but just jump directory only. //note that if view use double clicked signal, this signal should //not sended again. connect(m_view, &DirectoryViewWidget::updateWindowLocationRequest, this, [=](const QString &uri) { Q_EMIT this->updateWindowLocationRequest(uri, true); }); //m_proxy->switchView(view); m_layout->addWidget(dynamic_cast(view), Qt::AlignBottom); DirectoryViewFactoryManager2::getInstance()->setDefaultViewId(viewId); if (!selection.isEmpty()) { view->setSelections(selection); } QAction *cdUpAction = new QAction(m_view); cdUpAction->setShortcuts(QList()<cdUp(); }); this->addAction(cdUpAction); QAction *goBackAction = new QAction(m_view); goBackAction->setShortcut(QKeySequence::Back); connect(goBackAction, &QAction::triggered, this, [=]() { this->goBack(); }); this->addAction(goBackAction); QAction *goForwardAction = new QAction(m_view); goForwardAction->setShortcut(QKeySequence::Forward); connect(goForwardAction, &QAction::triggered, this, [=]() { this->goForward(); }); this->addAction(goForwardAction); QAction *editAction = new QAction(m_view); editAction->setShortcuts(QList()<getSelections(); bool hasStandardPath = FileUtils::containsStandardPath(selections); if (selections.count() == 1 && !hasStandardPath) { QString one = selections.first(); if(one.startsWith("filesafe:///") && one.remove("filesafe:///").indexOf("/") == -1) { return ; } m_view->editUri(selections.first()); } }); this->addAction(editAction); Q_EMIT viewTypeChanged(); } void DirectoryViewContainer::refresh() { if (!m_view) return; m_view->beginLocationChange(); } void DirectoryViewContainer::bindNewProxy(DirectoryViewProxyIface *proxy) { //disconnect old proxy //connect new proxy } const QStringList DirectoryViewContainer::getCurrentSelections() { if (m_view) return m_view->getSelections(); return QStringList(); } const QString DirectoryViewContainer::getCurrentUri() { if (m_view) { return m_view->getDirectoryUri(); } return nullptr; } const QStringList DirectoryViewContainer::getAllFileUris() { if (m_view) return m_view->getAllFileUris(); return QStringList(); } void DirectoryViewContainer::stopLoading() { if (m_view) { m_view->stopLocationChange(); Q_EMIT this->directoryChanged(); } } void DirectoryViewContainer::tryJump(int index) { QStringList l; l< index) { m_forward_list<getSortType(); return FileItemModel::ColumnType(type); } void DirectoryViewContainer::setSortType(FileItemModel::ColumnType type) { if (!m_view) return; m_view->setSortType(type); Peony::GlobalSettings::getInstance()->setValue(SORT_COLUMN, type); } Qt::SortOrder DirectoryViewContainer::getSortOrder() { if (!m_view) return Qt::AscendingOrder; int order = m_view->getSortOrder(); return Qt::SortOrder(order); } void DirectoryViewContainer::setSortOrder(Qt::SortOrder order) { if (order < 0) return; if (!m_view) return; Peony::GlobalSettings::getInstance()->setValue(SORT_ORDER, order); m_view->setSortOrder(order); } void DirectoryViewContainer::onViewDoubleClicked(const QString& uri) { } peony/libpeony-qt/controls/directory-view/delegate/0000755000175000017500000000000014205115226021462 5ustar fengfengpeony/libpeony-qt/controls/directory-view/delegate/icon-view-index-widget.h0000644000175000017500000000422414205101223026113 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #ifndef ICONVIEWINDEXWIDGET_H #define ICONVIEWINDEXWIDGET_H #include #include #include #include #include #include namespace Peony { class FileInfo; namespace DirectoryView { class IconViewDelegate; class IconViewIndexWidget : public QWidget { friend class IconViewDelegate; Q_OBJECT public: explicit IconViewIndexWidget(const IconViewDelegate *delegate, const QStyleOptionViewItem &option, const QModelIndex &index, QWidget *parent = nullptr); ~IconViewIndexWidget() override; bool eventFilter(QObject *watched, QEvent *event) override; protected: void paintEvent(QPaintEvent *e) override; void mousePressEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void mouseDoubleClickEvent(QMouseEvent *event) override; void adjustPos(); private: QStyleOptionViewItem m_option; QModelIndex m_index; QTextEdit *m_edit; const IconViewDelegate *m_delegate; std::weak_ptr m_info; QTimer m_edit_trigger; bool m_is_dragging = false; bool b_elide_text = false; const int ELIDE_TEXT_LENGTH = 32; }; } } #endif // ICONVIEWINDEXWIDGET_H peony/libpeony-qt/controls/directory-view/delegate/delegate.pri0000644000175000017500000000060314205115226023747 0ustar fengfengINCLUDEPATH += $$PWD HEADERS += \ $$PWD/icon-view-delegate.h \ $$PWD/icon-view-editor.h \ $$PWD/icon-view-index-widget.h \ $$PWD/side-bar-delegate.h \ $$PWD/list-view-delegate.h SOURCES += \ $$PWD/icon-view-delegate.cpp \ $$PWD/icon-view-editor.cpp \ $$PWD/icon-view-index-widget.cpp \ $$PWD/side-bar-delegate.cpp \ $$PWD/list-view-delegate.cpp peony/libpeony-qt/controls/directory-view/delegate/icon-view-editor.h0000644000175000017500000000257614205101223025021 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef ICONVIEWEDITOR_H #define ICONVIEWEDITOR_H #include #include "peony-core_global.h" class QLabel; class QLineEdit; namespace Peony { namespace DirectoryView { class PEONYCORESHARED_EXPORT IconViewEditor : public QTextEdit { Q_OBJECT public: explicit IconViewEditor(QWidget *parent = nullptr); ~IconViewEditor() override; Q_SIGNALS: void returnPressed(); public Q_SLOTS: void minimalAdjust(); protected: void paintEvent(QPaintEvent *e) override; void keyPressEvent(QKeyEvent *e) override; private: QLineEdit *m_styled_edit; }; } } #endif // ICONVIEWEDITOR_H peony/libpeony-qt/controls/directory-view/delegate/list-view-delegate.cpp0000644000175000017500000002334114205115226025664 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "list-view-delegate.h" #include "file-operation-manager.h" #include "file-rename-operation.h" #include "file-item-model.h" #include "list-view.h" #include "clipboard-utils.h" #include "file-info.h" #include #include #include #include #include #include #include using namespace Peony; ListViewDelegate::ListViewDelegate(QObject *parent) : QStyledItemDelegate(parent) { m_styled_button = new QPushButton; } ListViewDelegate::~ListViewDelegate() { m_styled_button->deleteLater(); } void ListViewDelegate::initIndexOption(QStyleOptionViewItem *option, const QModelIndex &index) const { return initStyleOption(option, index); } void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); opt.displayAlignment = Qt::Alignment(Qt::AlignLeft|Qt::AlignVCenter); auto view = qobject_cast(parent()); auto info = FileInfo::fromUri(index.data(Qt::UserRole).toString()); auto colors = info->getColors(); if (index.column() == 0 && colors.count() >0) { if (!view->isDragging() || !view->selectionModel()->selectedIndexes().contains(index)) { int xoffset = 5; int yoffset = 0; int index = 0; const int MAX_LABEL_NUM = 3; int startIndex = (colors.count() > MAX_LABEL_NUM ? colors.count() - MAX_LABEL_NUM : 0); //set color label on center, fix bug#40609 auto iconSize = view->iconSize(); auto size = iconSize.height()/2; auto labelSize = iconSize.height()/3; if (labelSize > 10) labelSize = 10; if (labelSize <6) labelSize = 6; if (colors.count() == 1) yoffset = size-labelSize/2; else if (colors.count() == 2) yoffset = size - labelSize; else yoffset = labelSize/2; for (int i = startIndex; i < colors.count(); ++i, ++index) { auto color = colors.at(i); painter->save(); painter->setRenderHint(QPainter::Antialiasing); painter->translate(0, opt.rect.topLeft().y()); painter->translate(2, 2); painter->setPen(opt.palette.highlightedText().color()); painter->setBrush(color); painter->drawEllipse(QRectF(xoffset, yoffset, labelSize, labelSize)); painter->restore(); yoffset += labelSize/2; } } } if (ClipboardUtils::isClipboardHasFiles() && FileUtils::isSamePath(ClipboardUtils::getClipedFilesParentUri(), view->getDirectoryUri())) { if (ClipboardUtils::isClipboardFilesBeCut()) { auto clipedUris = ClipboardUtils::getClipboardFilesUris(); if (clipedUris.contains(FileUtils::urlEncode(index.data(Qt::UserRole).toString()))) { painter->setOpacity(0.5); qDebug()<<"cut item in list view"<setOpacity(1.0); } } QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter); //add link and read only icon support if (index.column() == 0) { 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_y =rect.y(); //paint symbolic link emblems if (isSymbolicLink) { QIcon icon = QIcon::fromTheme("emblem-symbolic-link"); //qDebug()<symbolicIconName(); icon.paint(painter, loc_x, loc_y, size, size); //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()) { QIcon icon = QIcon::fromTheme("emblem-unreadable"); icon.paint(painter, loc_x, loc_y, size, size); } else if (!info->canWrite() && !info->canExecute()) { QIcon icon = QIcon::fromTheme("emblem-readonly"); icon.paint(painter, loc_x, loc_y, size, size); } } } } QWidget *ListViewDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { TextEdit *edit = new TextEdit(parent); edit->setAcceptRichText(false); //edit->setContextMenuPolicy(Qt::CustomContextMenu); edit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); edit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); edit->setWordWrapMode(QTextOption::NoWrap); QTimer::singleShot(1, parent, [=]() { this->updateEditorGeometry(edit, option, index); }); connect(edit, &TextEdit::textChanged, this, [=]() { updateEditorGeometry(edit, option, index); }); connect(edit, &TextEdit::finishEditRequest, this, [=]() { setModelData(edit, nullptr, index); edit->deleteLater(); }); return edit; } void ListViewDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { TextEdit *edit = qobject_cast(editor); if (!edit) return; edit->setText(index.data(Qt::DisplayRole).toString()); auto cursor = edit->textCursor(); cursor.setPosition(0, QTextCursor::MoveAnchor); cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); bool isDir = FileUtils::getFileIsFolder(index.data(Qt::UserRole).toString()); bool isDesktopFile = index.data(Qt::UserRole).toString().endsWith(".desktop"); bool isSoftLink = FileUtils::getFileIsSymbolicLink(index.data(Qt::UserRole).toString()); if (!isDesktopFile && !isSoftLink && !isDir && edit->toPlainText().contains(".") && !edit->toPlainText().startsWith(".")) { int n = 1; if(index.data(Qt::DisplayRole).toString().contains(".tar.")) //ex xxx.tar.gz xxx.tar.bz2 n = 2; while(n){ cursor.movePosition(QTextCursor::WordLeft, QTextCursor::KeepAnchor, 1); cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 1); --n; } } //qDebug()<setTextCursor(cursor); } void ListViewDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyledItemDelegate::updateEditorGeometry(editor, option, index); TextEdit *edit = qobject_cast(editor); edit->setFixedHeight(editor->height()); edit->resize(edit->document()->size().width(), -1); } void ListViewDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { TextEdit *edit = qobject_cast(editor); if (!edit) return; auto text = edit->toPlainText(); if (text.isEmpty()) return; //process special name . or .. or only space if (text == "." || text == ".." || text.trimmed() == "") return; if (! index.isValid()) return; auto view = qobject_cast(parent()); auto oldName = index.data(Qt::DisplayRole).toString(); if (text == oldName) { //create new file, should select the file or folder auto flags = QItemSelectionModel::Select|QItemSelectionModel::Rows; view->selectionModel()->select(index, flags); view->setFocus(); return; } auto fileOpMgr = FileOperationManager::getInstance(); auto renameOp = new FileRenameOperation(index.data(FileItemModel::UriRole).toString(), text); connect(renameOp, &FileRenameOperation::operationFinished, view, [=](){ auto info = renameOp->getOperationInfo().get(); auto uri = info->target(); QTimer::singleShot(100, view, [=](){ view->setSelections(QStringList()<scrollToSelection(uri); view->setFocus(); }); }, Qt::BlockingQueuedConnection); fileOpMgr->startOperation(renameOp, true); } QSize ListViewDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const { QSize size = QStyledItemDelegate::sizeHint(option, index); auto view = qobject_cast(parent()); int expectedHeight = view->iconSize().height() + 4; size.setHeight(qMax(expectedHeight, size.height())); return size; } //TextEdit TextEdit::TextEdit(QWidget *parent) : QTextEdit (parent) { } void TextEdit::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { Q_EMIT finishEditRequest(); return; } return QTextEdit::keyPressEvent(e); } peony/libpeony-qt/controls/directory-view/delegate/icon-view-delegate.cpp0000644000175000017500000004232014205115226025637 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #include "icon-view-delegate.h" #include "icon-view.h" #include "file-item-proxy-filter-sort-model.h" #include "file-item.h" #include "file-info.h" #include "file-operation-manager.h" #include "file-rename-operation.h" #include #include #include #include #include #include #include #include #include #include #include "icon-view-editor.h" #include "icon-view-index-widget.h" #include #include "clipboard-utils.h" #include #include #include using namespace Peony; using namespace Peony::DirectoryView; IconViewDelegate::IconViewDelegate(QObject *parent) : QStyledItemDelegate (parent) { m_styled_button = new QPushButton; } IconViewDelegate::~IconViewDelegate() { m_styled_button->deleteLater(); } QSize IconViewDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyleOptionViewItem opt = option; initStyleOption(&opt, index); auto view = qobject_cast(this->parent()); auto iconSize = view->iconSize(); auto font = qApp->font(); auto fm = QFontMetrics(font); int width = iconSize.width() + 41; int height = iconSize.height() + fm.ascent()*2 + 20; return QSize(width, height); /* qDebug()<save(); bool isDragging = false; auto view = qobject_cast(this->parent()); if (view->state() == IconView::DraggingState) { isDragging = true; if (view->selectionModel()->selection().contains(index)) { painter->setOpacity(0.8); } } //default painter //QStyledItemDelegate::paint(painter, option, index); QIcon icon = qvariant_cast(index.data(Qt::DecorationRole)); //qDebug()<style(); //qDebug()<iconSize(); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, nullptr); opt.decorationSize = rawDecoSize; bool bCutFile = false; if (ClipboardUtils::isClipboardHasFiles() && FileUtils::isSamePath(ClipboardUtils::getClipedFilesParentUri(), view->getDirectoryUri())) { if (ClipboardUtils::isClipboardFilesBeCut()) { auto clipedUris = ClipboardUtils::getClipboardFilesUris(); if (clipedUris.contains(FileUtils::urlEncode(index.data(FileItemModel::UriRole).toString()))) { painter->setOpacity(0.5); bCutFile = true; qDebug()<<"cut item"<setOpacity(1.0); auto iconSizeExpected = view->iconSize(); auto iconRect = style->subElementRect(QStyle::SE_ItemViewItemDecoration, &opt, opt.widget); int y_delta = iconSizeExpected.height() - iconRect.height(); opt.rect.setY(opt.rect.y() + y_delta); auto text = opt.text; opt.text = nullptr; style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget); opt.text = text; //auto textSize = IconViewTextHelper::getTextSizeForIndex(opt, index, 2, 2); painter->save(); painter->translate(opt.rect.topLeft()); painter->translate(0, iconRect.size().height() + 5); IconViewTextHelper::paintText(painter, opt, index, 9999, 2, 2); painter->restore(); //get file info from index auto model = static_cast(view->model()); auto item = model->itemFromIndex(index); //NOTE: item might be deleted when painting, because we might start a //location change during the painting. if (!item) { return; } auto info = item->info(); auto rect = view->visualRect(index); bool useIndexWidget = false; if (view->selectedIndexes().count() == 1 && view->selectedIndexes().first() == index && !bCutFile) { useIndexWidget = true; if (view->indexWidget(index)) { } else if (! view->isDraggingState() && view->m_allow_set_index_widget) { IconViewIndexWidget *indexWidget = new IconViewIndexWidget(this, option, index, getView()); connect(getView()->m_model, &FileItemModel::dataChanged, indexWidget, [=](const QModelIndex &topleft, const QModelIndex &bottomRight){ // if item has been removed and there is no reference for responding info, // clear index widgets. if (!indexWidget->m_info.lock()) { getView()->clearIndexWidget(); return; } if (topleft.data(Qt::UserRole).toString() == indexWidget->m_info.lock().get()->uri()) { if (getView()->getSelections().count() == 1 && getView()->getSelections().first() == topleft.data(Qt::UserRole).toString()) { auto selections = getView()->getSelections(); getView()->clearSelection(); getView()->setSelections(selections); } } }); view->setIndexWidget(index, indexWidget); indexWidget->adjustPos(); } } //fix bug#46785, select one file cut has no effect issue if (bCutFile && !getView()->getDelegateEditFlag())/* Rename is index is not set to nullptr,link to bug#61119.modified by 2021/06/22 */ view->setIndexWidget(index, nullptr); // draw color symbols if (!isDragging || !view->selectedIndexes().contains(index)) { auto colors = info->getColors(); int offset = 0; const int MAX_LABEL_NUM = 3; int startIndex = (colors.count() > MAX_LABEL_NUM ? colors.count() - MAX_LABEL_NUM : 0); for (int i = startIndex; i < colors.count(); ++i) { auto color = colors.at(i); painter->save(); painter->setRenderHint(QPainter::Antialiasing); painter->translate(option.rect.topLeft()); painter->translate(2, 2); painter->setPen(opt.palette.highlightedText().color()); painter->setBrush(color); painter->drawEllipse(QRectF(offset, 0, 10, 10)); painter->restore(); offset += 10/2; } } //paint symbolic link emblems if (info->isSymbolLink()) { QIcon icon = QIcon::fromTheme("emblem-symbolic-link"); //qDebug()<symbolicIconName(); icon.paint(painter, rect.x() + rect.width() - 30, rect.y() + 10, 20, 20, Qt::AlignCenter); } //paint access emblems //NOTE: we can not query the file attribute in smb:///(samba) and network:///. if (info->uri().startsWith("file:")) { if (!info->canRead()) { QIcon icon = QIcon::fromTheme("emblem-unreadable"); icon.paint(painter, rect.x() + 10, rect.y() + 10, 20, 20); } else if (!info->canWrite() && !info->canExecute()) { QIcon icon = QIcon::fromTheme("emblem-readonly"); icon.paint(painter, rect.x() + 10, rect.y() + 10, 20, 20); } painter->restore(); return; } painter->restore(); //single selection, we have to repaint the emblems. } void IconViewDelegate::setCutFiles(const QModelIndexList &indexes) { m_cut_indexes = indexes; } void IconViewDelegate::doneWithEditor() { auto editor = qobject_cast(sender()); commitData(editor); closeEditor(editor, NoHint); isEditing(false); } QWidget *IconViewDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option); Q_UNUSED(index); auto edit = new IconViewEditor(parent); edit->setContentsMargins(0, 0, 0, 0); edit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); edit->setMinimumSize(sizeHint(option, index).width(), 54); edit->setText(index.data(Qt::DisplayRole).toString()); edit->setAlignment(Qt::AlignCenter); //NOTE: if we directly call this method, there will be //nothing happen. add a very short delay will ensure that //the edit be resized. QTimer::singleShot(1, edit, [=]() { edit->minimalAdjust(); }); connect(edit, &IconViewEditor::returnPressed, this, &IconViewDelegate::doneWithEditor); connect(edit, &QWidget::destroyed, this, [=]() { // NOTE: resort view after edit closed. // it's because if we not, the viewport might // not be updated in some cases. Q_EMIT isEditing(false); #if QT_VERSION > QT_VERSION_CHECK(5, 12, 0) QTimer::singleShot(100, this, [=]() { #else QTimer::singleShot(100, [=]() { #endif auto model = qobject_cast(getView()->model()); //fix rename file back to default sort order //model->sort(-1, Qt::SortOrder(getView()->getSortOrder())); //model->sort(getView()->getSortType(), Qt::SortOrder(getView()->getSortOrder())); }); }); return edit; } void IconViewDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { IconViewEditor *edit = qobject_cast(editor); if (!edit) return; Q_EMIT isEditing(true); auto cursor = edit->textCursor(); cursor.setPosition(0, QTextCursor::MoveAnchor); cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor); bool isDir = FileUtils::getFileIsFolder(index.data(Qt::UserRole).toString()); bool isDesktopFile = index.data(Qt::UserRole).toString().endsWith(".desktop"); bool isSoftLink = FileUtils::getFileIsSymbolicLink(index.data(Qt::UserRole).toString()); if (!isDesktopFile && !isSoftLink && !isDir && edit->toPlainText().contains(".") && !edit->toPlainText().startsWith(".")) { int n = 1; if(index.data(Qt::DisplayRole).toString().contains(".tar.")) //ex xxx.tar.gz xxx.tar.bz2 n = 2; while(n){ cursor.movePosition(QTextCursor::WordLeft, QTextCursor::KeepAnchor, 1); cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 1); --n; } } //qDebug()<setTextCursor(cursor); } void IconViewDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyledItemDelegate::updateEditorGeometry(editor, option, index); auto edit = qobject_cast(editor); if (!edit) return; auto opt = option; auto iconExpectedSize = getView()->iconSize(); //auto iconRect = opt.widget->style()->subElementRect(QStyle::SE_ItemViewItemDecoration, &opt, opt.widget); //auto y_delta = iconExpectedSize.height() - iconRect.height(); //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); } void IconViewDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { IconViewEditor *edit = qobject_cast(editor); if (!edit) return; auto newName = edit->toPlainText(); auto oldName = index.data(Qt::DisplayRole).toString(); if (newName.isNull()) return; //process special name . or .. or only space if (newName == "." || newName == ".." || newName.trimmed() == "") newName = ""; //comment new name != suffix check to fix feedback issue if (newName.length() >0 && newName != oldName/* && newName != suffix*/) { auto fileOpMgr = FileOperationManager::getInstance(); auto renameOp = new FileRenameOperation(index.data(FileItemModel::UriRole).toString(), newName); connect(renameOp, &FileRenameOperation::operationFinished, getView(), [=](){ auto info = renameOp->getOperationInfo().get(); auto uri = info->target(); QTimer::singleShot(100, getView(), [=](){ getView()->setSelections(QStringList()<scrollToSelection(uri); //set focus to fix bug#54061 getView()->setFocus(); }); }, Qt::BlockingQueuedConnection); fileOpMgr->startOperation(renameOp, true); } else if (newName == oldName) { //create new file, should select the file or folder getView()->selectionModel()->select(index, QItemSelectionModel::Select); //set focus to fix bug#54061 getView()->setFocus(); } } void IconViewDelegate::setIndexWidget(const QModelIndex &index, QWidget *widget) const { auto view = qobject_cast(this->parent()); view->setIndexWidget(index, widget); } IconView *IconViewDelegate::getView() const { return qobject_cast(parent()); } const QBrush IconViewDelegate::selectedBrush() const { return m_styled_button->palette().highlight(); } QSize IconViewTextHelper::getTextSizeForIndex(const QStyleOptionViewItem &option, const QModelIndex &index, int horizalMargin, int maxLineCount) { int fixedWidth = option.rect.width() - horizalMargin*2; QString text = option.text; QFont font = option.font; QFontMetrics fontMetrics = option.fontMetrics; int lineSpacing = fontMetrics.lineSpacing(); int textHight = 0; QTextLayout textLayout(text, font); QTextOption opt; opt.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); opt.setAlignment(Qt::AlignHCenter); textLayout.setTextOption(opt); textLayout.beginLayout(); while (true) { QTextLine line = textLayout.createLine(); if (!line.isValid()) break; line.setLineWidth(fixedWidth); textHight += lineSpacing; } textLayout.endLayout(); if (maxLineCount > 0) { textHight = qMin(maxLineCount * lineSpacing, textHight); } 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) { painter->save(); painter->translate(horizalMargin, 0); if (useSystemPalette) { if (option.state.testFlag(QStyle::State_Selected)) painter->setPen(option.palette.highlightedText().color()); else painter->setPen(option.palette.text().color()); } if (customColor != Qt::transparent) { painter->setPen(customColor); } int lineCount = 0; QString text = option.text; QFont font = option.font; QFontMetrics fontMetrics = option.fontMetrics; int lineSpacing = fontMetrics.lineSpacing(); QTextLayout textLayout(text, font); QTextOption opt; opt.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); opt.setAlignment(Qt::AlignHCenter); textLayout.setTextOption(opt); textLayout.beginLayout(); int width = option.rect.width() - 2*horizalMargin; int y = 0; while (true) { QTextLine line = textLayout.createLine(); if (!line.isValid()) break; line.setLineWidth(width); int nextLineY = y + lineSpacing; lineCount++; if (textMaxHeight >= nextLineY + lineSpacing && lineCount != maxLineCount) { line.draw(painter, QPoint(0, y)); y = nextLineY; } else { QString lastLine = option.text.mid(line.textStart()); QString elidedLastLine = fontMetrics.elidedText(lastLine, Qt::ElideRight, width); auto rect = QRect(horizalMargin, y /*+ fontMetrics.ascent()*/, width, textMaxHeight); //opt.setWrapMode(QTextOption::NoWrap); opt.setWrapMode(QTextOption::NoWrap); painter->drawText(rect, elidedLastLine, opt); //painter->drawText(QPoint(0, y + fontMetrics.ascent()), elidedLastLine); line = textLayout.createLine(); break; } } textLayout.endLayout(); painter->restore(); } peony/libpeony-qt/controls/directory-view/delegate/list-view-delegate.h0000644000175000017500000000443414205115226025333 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef LISTVIEWDELEGATE_H #define LISTVIEWDELEGATE_H #include #include #include "peony-core_global.h" class QPushButton; namespace Peony { class TextEdit; class ListViewDelegate : public QStyledItemDelegate { friend class ListView; Q_OBJECT public: explicit ListViewDelegate(QObject *parent = nullptr); ~ListViewDelegate() override; void initIndexOption(QStyleOptionViewItem *option, const QModelIndex &index) const; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; //QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; //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 setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const override; private: QPushButton *m_styled_button; }; class TextEdit : public QTextEdit { Q_OBJECT public: explicit TextEdit(QWidget *parent = nullptr); Q_SIGNALS: void finishEditRequest(); protected: void keyPressEvent(QKeyEvent *e); }; } #endif // LISTVIEWDELEGATE_H peony/libpeony-qt/controls/directory-view/delegate/side-bar-delegate.cpp0000644000175000017500000001243514205115226025431 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "side-bar-delegate.h" #include "side-bar.h" #include "side-bar-proxy-filter-sort-model.h" #include "side-bar-abstract-item.h" #include "side-bar-separator-item.h" #include #include #include using namespace Peony; SideBarDelegate::SideBarDelegate(QObject *parent) : QStyledItemDelegate(parent) { m_styled_button = new QPushButton; } SideBarDelegate::~SideBarDelegate() { m_styled_button->deleteLater(); } void SideBarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { //return QStyledItemDelegate::paint(painter, option, index); QStyleOptionViewItem opt = option; initStyleOption(&opt, index); //draw separator SideBar *sideBar = qobject_cast(this->parent()); SideBarProxyFilterSortModel *model = qobject_cast(sideBar->model()); auto item = model->itemFromIndex(index); if (item->type() == SideBarAbstractItem::SeparatorItem) { SideBarSeparatorItem *separator = qobject_cast(item); if (separator->separatorType() != SideBarSeparatorItem::EmptyFile) { auto visualRect = sideBar->visualRect(index); visualRect.setX(0); painter->fillRect(visualRect, opt.widget->palette().brush(QPalette::Base)); return; } } //FIXME: maybe i should use qss "show-decoration-selected" instead if (index.column() != 0 || !index.isValid()) { if (opt.state.testFlag(QStyle::State_Selected)) { painter->fillRect(opt.rect, m_styled_button->palette().highlight()); } else if (opt.state.testFlag(QStyle::State_MouseOver)) { QColor color = m_styled_button->palette().highlight().color(); color.setAlpha(127); painter->fillRect(opt.rect, color); } return QStyledItemDelegate::paint(painter, option, index); } auto sideBarView = sideBar; auto visualRect = sideBarView->visualRect(index); visualRect.setX(0); painter->fillRect(visualRect, opt.widget->palette().brush(QPalette::Base)); int x = opt.rect.x(); opt.rect.setX(0); if (opt.state.testFlag(QStyle::State_Selected)) { painter->fillRect(opt.rect, m_styled_button->palette().highlight()); } else if (opt.state.testFlag(QStyle::State_MouseOver)) { QColor color = m_styled_button->palette().highlight().color(); color.setAlpha(127); painter->fillRect(opt.rect, color); } //sideBarView->style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, sideBarView); opt.rect.setX(x + 7); auto iconRect = QRect(opt.rect.topLeft() - QPoint(opt.rect.height(), 0), QSize(opt.rect.height(), opt.rect.height())); iconRect.adjust(8, 8, -8, -8); iconRect.moveTo(iconRect.topLeft() + QPoint(6, 0)); if (sideBarView->model()->hasChildren(index)) { if (sideBarView->isExpanded(index)) { auto icon = QIcon::fromTheme("ukui-down-symbolic", QIcon(":/icons/ukui-down-symbolic")); icon.paint(painter, iconRect, Qt::AlignCenter); } else { auto icon = QIcon::fromTheme("ukui-end-symbolic", QIcon(":/icons/ukui-end-symbolic")); icon.paint(painter, iconRect); } } sideBarView->style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, sideBarView); } QSize SideBarDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { //separator SideBar *sideBar = qobject_cast(this->parent()); SideBarProxyFilterSortModel *model = qobject_cast(sideBar->model()); auto item = model->itemFromIndex(index); if (item->type() == SideBarAbstractItem::SeparatorItem) { auto separatorItem = static_cast(item); int height = 0; auto size = QStyledItemDelegate::sizeHint(option, index); switch (separatorItem->separatorType()) { case SideBarSeparatorItem::Large: { height = 15; break; } case SideBarSeparatorItem::Small: { height = 12; break; } case SideBarSeparatorItem::EmptyFile: { height = 28; break; } } size.setHeight(height); return size; } if (index.column() != 0) return QStyledItemDelegate::sizeHint(option, index); auto size = QStyledItemDelegate::sizeHint(option, index); size.setHeight(28); size.setWidth(size.width() + 10); return size; } peony/libpeony-qt/controls/directory-view/delegate/side-bar-delegate.h0000644000175000017500000000352714205115226025100 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef SIDEBARDELEGATE_H #define SIDEBARDELEGATE_H #include #include "peony-core_global.h" class QPushButton; namespace Peony { class PEONYCORESHARED_EXPORT SideBarDelegate : public QStyledItemDelegate { Q_OBJECT public: explicit SideBarDelegate(QObject *parent = nullptr); ~SideBarDelegate() override; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; /* //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 setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; */ private: QPushButton *m_styled_button; }; } #endif // SIDEBARDELEGATE_H peony/libpeony-qt/controls/directory-view/delegate/icon-view-index-widget.cpp0000644000175000017500000002410214205115226026453 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2019, Tianjin KYLIN Information Technology 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 . * * Authors: Yue Lan * */ #include "icon-view-index-widget.h" #include "icon-view-delegate.h" #include "icon-view.h" #include #include #include #include #include #include #include #include #include #include "file-info.h" #include "file-item-proxy-filter-sort-model.h" #include "file-item.h" #include using namespace Peony; using namespace Peony::DirectoryView; IconViewIndexWidget::IconViewIndexWidget(const IconViewDelegate *delegate, const QStyleOptionViewItem &option, const QModelIndex &index, QWidget *parent) : QWidget(parent) { installEventFilter(parent); setMouseTracking(true); m_edit_trigger.setInterval(3000); m_edit_trigger.setSingleShot(true); QTimer::singleShot(750, this, [=]() { m_edit_trigger.start(); }); //use QTextEdit to show full file name when select m_edit = new QTextEdit(); m_option = option; m_index = index; m_delegate = delegate; m_delegate->getView()->m_renameTimer->stop(); m_delegate->getView()->m_editValid = false; m_delegate->getView()->m_renameTimer->start(); m_is_dragging = m_delegate->getView()->isDraggingState(); QSize size = delegate->sizeHint(option, index); setMinimumSize(size); //extra emblems auto proxy_model = static_cast(delegate->getView()->model()); auto item = proxy_model->itemFromIndex(index); if (item) { m_info = item->info(); } m_delegate->initStyleOption(&m_option, m_index); #if (QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)) m_option.features.setFlag(QStyleOptionViewItem::WrapText); #else m_option.features |= QStyleOptionViewItem::WrapText; #endif m_option.textElideMode = Qt::ElideNone; auto opt = m_option; //opt.rect.setHeight(9999); opt.rect.moveTo(0, 0); //qDebug()<getView()->iconSize(); QRect iconRect = QApplication::style()->subElementRect(QStyle::SE_ItemViewItemDecoration, &opt, opt.widget); auto y_delta = iconExpectedSize.height() - iconRect.height(); opt.rect.moveTo(opt.rect.x(), opt.rect.y() + y_delta); m_option = opt; adjustPos(); auto textSize = IconViewTextHelper::getTextSizeForIndex(opt, index, 2); int fixedHeight = 5 + iconExpectedSize.height() + 5 + textSize.height() + 5; int y_bottom = option.rect.y() + fixedHeight + 20; //qDebug() << "Y:" <getView()->height(); b_elide_text = false; if ( y_bottom > m_delegate->getView()->height() && opt.text.length() > ELIDE_TEXT_LENGTH) { b_elide_text = true; int charWidth = opt.fontMetrics.averageCharWidth(); opt.text = opt.fontMetrics.elidedText(opt.text, Qt::ElideRight, ELIDE_TEXT_LENGTH * charWidth); //recount size textSize = IconViewTextHelper::getTextSizeForIndex(opt, index, 2); fixedHeight = 5 + iconExpectedSize.height() + 5 + textSize.height() + 5; } if (fixedHeight >= option.rect.height()) setFixedHeight(fixedHeight); else setFixedHeight(option.rect.height()); m_option.rect.setHeight(fixedHeight - y_delta); #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) connect(qApp, &QApplication::fontChanged, this, [=]() { m_delegate->getView()->setIndexWidget(m_index, nullptr); }); #else //FIXME: handle font change. #endif } IconViewIndexWidget::~IconViewIndexWidget() { delete m_edit; } bool IconViewIndexWidget::eventFilter(QObject *watched, QEvent *event) { if (event->type() == QEvent::Resize) { adjustPos(); update(); } return false; } void IconViewIndexWidget::paintEvent(QPaintEvent *e) { IconView *view = m_delegate->getView(); auto expectedRect = view->visualRect(m_index); auto rectPos = this->mapToParent(QPoint(0, 0)); if (expectedRect.topLeft() != rectPos) { adjustPos(); update(); return; } QWidget::paintEvent(e); QPainter p(this); //p.fillRect(0, 0, 999, 999, Qt::red); //adjustPos(); //qDebug()<size() << m_delegate->getView()->iconSize(); auto opt = m_option; auto rawRect = m_option.rect; opt.rect = this->rect(); //p.fillRect(opt.rect, m_delegate->selectedBrush()); auto rawDecoSize = opt.decorationSize; opt.decorationSize = m_delegate->getView()->iconSize(); QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, &p, nullptr); opt.decorationSize = rawDecoSize; opt.rect = rawRect; auto tmp = opt.text; opt.text = nullptr; QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, &p, opt.widget); if (b_elide_text) { int charWidth = opt.fontMetrics.averageCharWidth(); tmp = opt.fontMetrics.elidedText(tmp, Qt::ElideRight, ELIDE_TEXT_LENGTH * charWidth); } opt.text = std::move(tmp); //auto textRectF = QRectF(0, m_delegate->getView()->iconSize().height(), this->width(), this->height()); p.save(); p.setPen(opt.palette.highlightedText().color()); p.translate(0, m_delegate->getView()->iconSize().height() + 5); IconViewTextHelper::paintText(&p, opt, m_index, 9999, 2, 4); // p.translate(-1, m_delegate->getView()->iconSize().height() + 13); // //m_edit->document()->drawContents(&p); // QTextOption textOption(Qt::AlignTop|Qt::AlignHCenter); // textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); // p.setFont(opt.font); // p.setPen(opt.palette.highlightedText().color()); // p.drawText(QRect(1, 0, this->width() - 1, 9999), opt.text, textOption); p.restore(); //extra emblems if (!m_info.lock()) { return; } auto info = m_info.lock(); // draw color symbols auto colors = info->getColors(); int offset = 0; const int MAX_LABEL_NUM = 3; int startIndex = (colors.count() > MAX_LABEL_NUM ? colors.count() - MAX_LABEL_NUM : 0); for (int i = startIndex; i < colors.count(); ++i) { auto color = colors.at(i); p.save(); p.setRenderHint(QPainter::Antialiasing); p.translate(2, 2); p.setPen(opt.palette.highlightedText().color()); p.setBrush(color); p.drawEllipse(QRectF(offset, 0, 10, 10)); p.restore(); offset += 10/2; } //paint symbolic link emblems if (info->isSymbolLink()) { QIcon icon = QIcon::fromTheme("emblem-symbolic-link"); //qDebug()<< "symbolic:" << info->symbolicIconName(); icon.paint(&p, this->width() - 30, 10, 20, 20, Qt::AlignCenter); } //paint access emblems //NOTE: we can not query the file attribute in smb:///(samba) and network:///. if (!info->uri().startsWith("file:")) { return; } auto rect = this->rect(); if (!info->canRead()) { QIcon icon = QIcon::fromTheme("emblem-unreadable"); icon.paint(&p, rect.x() + 10, rect.y() + 10, 20, 20); } else if (!info->canWrite() && !info->canExecute()) { QIcon icon = QIcon::fromTheme("emblem-readonly"); icon.paint(&p, rect.x() + 10, rect.y() + 10, 20, 20); } } void IconViewIndexWidget::mousePressEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton) { IconView *view = m_delegate->getView(); if (view->isDraggingState() || m_is_dragging) { view->m_renameTimer->stop(); view->m_editValid = false; return QWidget::mousePressEvent(e); } view->m_editValid = true; if (view->m_renameTimer->isActive()) { if (view->m_renameTimer->remainingTime() < 3000 - qApp->styleHints()->mouseDoubleClickInterval() && view->m_renameTimer->remainingTime() > 0) { view->slotRename(); } else { view->m_editValid = false; view->m_renameTimer->stop(); } } else { view->m_editValid = false; view->m_renameTimer->start(); } e->accept(); return; // if (m_edit_trigger.isActive()) { // qDebug()<<"IconViewIndexWidget::mousePressEvent: edit"<type(); // m_delegate->getView()->setIndexWidget(m_index, nullptr); // m_delegate->getView()->edit(m_index); // return; // } } QWidget::mousePressEvent(e); } void IconViewIndexWidget::mouseMoveEvent(QMouseEvent *e) { QWidget::mouseMoveEvent(e); //comment to fix click selected file name unselect file, when file has long name //m_is_dragging = true; } void IconViewIndexWidget::mouseReleaseEvent(QMouseEvent *e) { QWidget::mouseReleaseEvent(e); //m_is_dragging = false; } void IconViewIndexWidget::mouseDoubleClickEvent(QMouseEvent *event) { m_delegate->getView()->activated(m_index); return; } void IconViewIndexWidget::adjustPos() { IconView *view = m_delegate->getView(); if (m_index.model() != view->model()) return; if (!view->selectionModel()->selectedIndexes().contains(m_index)) { this->close(); return; } auto visualRect = view->visualRect(m_index); if (this->mapToParent(QPoint()) != visualRect.topLeft()) this->move(visualRect.topLeft()); } peony/libpeony-qt/controls/directory-view/delegate/icon-view-editor.cpp0000644000175000017500000000423714205115226025360 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include #include #include #include #include #include "icon-view-editor.h" using namespace Peony; using namespace DirectoryView; IconViewEditor::IconViewEditor(QWidget *parent) : QTextEdit(parent) { setAcceptRichText(false); //setContextMenuPolicy(Qt::CustomContextMenu); m_styled_edit = new QLineEdit; setContentsMargins(0, 0, 0, 0); setAlignment(Qt::AlignCenter); // setStyleSheet("padding: 0px;" // "background-color: white;"); connect(this, &IconViewEditor::textChanged, this, &IconViewEditor::minimalAdjust); } IconViewEditor::~IconViewEditor() { m_styled_edit->deleteLater(); } void IconViewEditor::paintEvent(QPaintEvent *e) { QPainter p(this->viewport()); p.fillRect(this->viewport()->rect(), m_styled_edit->palette().base()); QPen pen; pen.setWidth(2); pen.setColor(this->palette().highlight().color()); QPolygon polygon = this->viewport()->rect(); p.setPen(pen); p.drawPolygon(polygon); QTextEdit::paintEvent(e); } void IconViewEditor::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { Q_EMIT returnPressed(); return; } QTextEdit::keyPressEvent(e); } void IconViewEditor::minimalAdjust() { this->resize(QSize(document()->size().width(), document()->size().height() + 10)); } peony/libpeony-qt/controls/directory-view/delegate/icon-view-delegate.h0000644000175000017500000000664514205113763025322 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef ICONVIEWDELEGATE_H #define ICONVIEWDELEGATE_H #include #include class QPushButton; namespace Peony { class DesktopIconViewDelegate; class DesktopIndexWidget; namespace DirectoryView { class IconView; class IconViewIndexWidget; class IconViewTextHelper; class IconViewDelegate : public QStyledItemDelegate { friend class IconViewIndexWidget; Q_OBJECT public: explicit IconViewDelegate(QObject *parent = nullptr); ~IconViewDelegate() override; IconView *getView() const; void setMaxLineCount(int count = 0); const QBrush selectedBrush() const; Q_SIGNALS: void isEditing(bool editing) const; void requestDone(QWidget *editor); public Q_SLOTS: void setCutFiles(const QModelIndexList &indexes); void doneWithEditor(); protected: void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; //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 setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; void setIndexWidget(const QModelIndex &index, QWidget *widget) const; private: QModelIndexList m_cut_indexes; QModelIndex m_index_widget_index; QWidget *m_index_widget; QPushButton *m_styled_button; }; class PEONYCORESHARED_EXPORT IconViewTextHelper { friend class IconViewDelegate; friend class IconViewIndexWidget; friend class Peony::DesktopIndexWidget; friend class Peony::DesktopIconViewDelegate; /*! * \brief getTextRectForIndex * \return the adjusted text rect to be painted. */ static QSize getTextSizeForIndex(const QStyleOptionViewItem &option, const QModelIndex &index, int horizalMargin = 0, int maxLineCount = 4); static void paintText(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index, int textMaxHeight, int horizalMargin = 0, int maxLineCount = 4, bool useSystemPalette = true, const QColor &customColor = Qt::transparent); }; } } #endif // ICONVIEWDELEGATE_H peony/libpeony-qt/controls/directory-view/directory-view.pri0000644000175000017500000000047114205101223023372 0ustar fengfengINCLUDEPATH += $$PWD include(directory-view-factory/directory-view-factory.pri) include(view/view.pri) include(delegate/delegate.pri) HEADERS += \ $$PWD/directory-view-container.h \ $$PWD/directory-view-widget.h SOURCES += \ $$PWD/directory-view-container.cpp \ $$PWD/directory-view-widget.cpp peony/libpeony-qt/controls/directory-view/directory-view-widget.h0000644000175000017500000001011714205115226024316 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef DIRECTORYVIEWWIDGET_H #define DIRECTORYVIEWWIDGET_H #include "peony-core_global.h" #include namespace Peony { class FileItemModel; class FileItemProxyFilterSortModel; class FileItemModel; class FileItemProxyFilterSortModel; /*! * \brief The DirectoryViewWidget class * * \details * This class is an instantiable interface. * * DirectoryViewWidget define a set of signals and slots for extensionable directory view * implement. If you want to custom a directory view, you should derive this class, * override it slots, and emit the signals at the appropriate time. * \see IconView2, ListView2 * * DirectoryViewWidget is a return value of DirectoryViewPluginIface2::create(). * If you want to make a directory view plugin, you should also create a libraries project and * derive both DirectoryViewPluginIface2 and DirectoryViewWidget. */ class PEONYCORESHARED_EXPORT DirectoryViewWidget : public QWidget { Q_OBJECT public: explicit DirectoryViewWidget(QWidget *parent = nullptr); virtual ~DirectoryViewWidget() {} const virtual QString viewId() { return "Directory View"; } //location const virtual QString getDirectoryUri() { return nullptr; } //selections const virtual QStringList getSelections() { return QStringList(); } //children const virtual QStringList getAllFileUris() { return QStringList(); } virtual int getSortType() { return 0; } virtual Qt::SortOrder getSortOrder() { return Qt::AscendingOrder; } //zoom virtual int currentZoomLevel() { return -1; } virtual int minimumZoomLevel() { return -1; } virtual int maximumZoomLevel() { return -1; } virtual bool supportZoom() { return false; } Q_SIGNALS: //loaction //FIXME: support open in new TAB? void viewDoubleClicked(const QString &uri); void viewDirectoryChanged(); void viewSelectionChanged(); void sortTypeChanged(int type); void sortOrderChanged(Qt::SortOrder order); //zoom in/out void zoomRequest(bool isZoomIn); //menu void menuRequest(const QPoint &pos); //window void updateWindowLocationRequest(const QString &uri); void updateWindowSelectionRequest(const QStringList &uris); public Q_SLOTS: virtual void bindModel(FileItemModel *model, FileItemProxyFilterSortModel *proxyModel) {} //location //virtual void open(const QStringList &uris, bool newWindow) {} virtual void setDirectoryUri(const QString &uri) {} virtual void beginLocationChange() {} virtual void stopLocationChange() {} virtual void closeDirectoryView() {} //selections virtual void setSelections(const QStringList &uris) {} virtual void invertSelections() {} virtual void scrollToSelection(const QString &uri) {} //clipboard //cut items should be drawn differently. virtual void setCutFiles(const QStringList &uris) {} virtual void setSortType(int sortType) {} virtual void setSortOrder(int sortOrder) {} virtual void editUri(const QString &uri) {} virtual void editUris(const QStringList uris) {} virtual void repaintView() {} virtual void clearIndexWidget() {} //zoom virtual void setCurrentZoomLevel(int zoomLevel) {} }; } #endif // DIRECTORYVIEWWIDGET_H peony/libpeony-qt/controls/directory-view/directory-view-container.h0000644000175000017500000001114714205115226025021 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * Authors: Meihong He * */ #ifndef DIRECTORYVIEWCONTAINER_H #define DIRECTORYVIEWCONTAINER_H #include "peony-core_global.h" #include #include #include #include "file-item-model.h" class QVBoxLayout; namespace Peony { class FileItemModel; class FileItemProxyFilterSortModel; class DirectoryViewProxyIface; class DirectoryViewWidget; /*! * \brief The DirectoryViewContainer class * \details * This class define a set of higher level operation for the directory view, * such as history functions and view switch. * In peony-qt, tab-window and view-type-switch are reserved from peony. * This class is mainly to archive these higher ui logic. * * A directory view can not swith it type itself because it was created * from its own factory, and it aslo could and should not contain more details * implement above the view it self. Those should be implement by a wrapper class * in higher level. * You can interpret it as the middleware of window and directory view. */ class PEONYCORESHARED_EXPORT DirectoryViewContainer : public QWidget { Q_OBJECT public: explicit DirectoryViewContainer(QWidget *parent = nullptr); ~DirectoryViewContainer(); const QString getCurrentUri(); const QStringList getCurrentSelections(); const QStringList getAllFileUris(); const QStringList getBackList(); const QStringList getForwardList(); bool canGoBack(); bool canGoForward(); bool canCdUp(); FileItemModel::ColumnType getSortType(); Qt::SortOrder getSortOrder(); //DirectoryViewProxyIface *getProxy() {return m_proxy;} DirectoryViewWidget *getView() { return m_view; } Q_SIGNALS: void viewTypeChanged(); void directoryChanged(); void selectionChanged(); void viewDoubleClicked(const QString &uri); void updateWindowLocationRequest(const QString &uri, bool addHistory, bool forceUpdate = false); void updateWindowSelectionRequest(const QStringList &uris); void menuRequest(const QPoint &pos); void zoomRequest(bool zoomIn); void setZoomLevelRequest(int zoomLevel); void updateStatusBarSliderStateRequest(); public Q_SLOTS: void goToUri(const QString &uri, bool addHistory, bool forceUpdate = false); void switchViewType(const QString &viewId); void goBack(); void goForward(); void cdUp(); void refresh(); void stopLoading(); void tryJump(int index); void clearHistory() { m_back_list.clear(); m_forward_list.clear(); } void setSortType(FileItemModel::ColumnType type); void setSortOrder(Qt::SortOrder order); void setSortFilter(int FileTypeIndex=0, int FileMTimeIndex=0, int FileSizeIndex=0); void setShowHidden(bool showHidden = false); void setUseDefaultNameSortOrder(bool use); void setSortFolderFirst(bool folderFirst); void setFilterLabelConditions(QString name); //mutiple filter conditions for new advance search void addFileNameFilter(QString key, bool updateNow = false); void addFilterCondition(int option, int classify, bool updateNow = false); void removeFilterCondition(int option, int classify, bool updateNow = false); void clearConditions(); void updateFilter(); void onViewDoubleClicked(const QString &uri); protected: /*! * \brief bindNewProxy * \param proxy * \deprecated * This method is deprecated and useless. It should be removed. */ void bindNewProxy(DirectoryViewProxyIface *proxy); private: QString m_current_uri; DirectoryViewProxyIface *m_proxy = nullptr; DirectoryViewWidget *m_view = nullptr; QStringList m_back_list; QStringList m_forward_list; QVBoxLayout *m_layout; FileItemModel *m_model; FileItemProxyFilterSortModel *m_proxy_model; QGSettings* m_control_center_plugin = nullptr; }; } #endif // DIRECTORYVIEWCONTAINER_H peony/libpeony-qt/controls/directory-view/directory-view-factory/0000755000175000017500000000000014205115226024331 5ustar fengfengpeony/libpeony-qt/controls/directory-view/directory-view-factory/directory-view-factory-manager.cpp0000644000175000017500000001007714205115226033073 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "directory-view-factory-manager.h" #include "directory-view-plugin-iface.h" #include "icon-view-factory.h" #include "list-view-factory.h" #include "global-settings.h" #include using namespace Peony; static DirectoryViewFactoryManager2 *globalInstance2 = nullptr; //2 DirectoryViewFactoryManager2* DirectoryViewFactoryManager2::getInstance() { if (!globalInstance2) { globalInstance2 = new DirectoryViewFactoryManager2; } return globalInstance2; } DirectoryViewFactoryManager2::DirectoryViewFactoryManager2(QObject *parent) : QObject(parent) { m_settings = GlobalSettings::getInstance(); m_hash = new QHash(); //register icon view and list view auto iconViewFactory2 = IconViewFactory2::getInstance(); registerFactory(iconViewFactory2->viewIdentity(), iconViewFactory2); m_internal_views<<"Icon View"; auto listViewFactory2 = ListViewFactory2::getInstance(); registerFactory(listViewFactory2->viewIdentity(), listViewFactory2); m_internal_views<<"List View"; } DirectoryViewFactoryManager2::~DirectoryViewFactoryManager2() { } void DirectoryViewFactoryManager2::registerFactory(const QString &name, DirectoryViewPluginIface2 *factory) { if (m_hash->value(name)) { return; } m_hash->insert(name, factory); } QStringList DirectoryViewFactoryManager2::getFactoryNames() { return m_hash->keys(); } DirectoryViewPluginIface2 *DirectoryViewFactoryManager2::getFactory(const QString &name) { return m_hash->value(name); } const QString DirectoryViewFactoryManager2::getDefaultViewId(const QString &uri) { if (m_default_view_id_cache.isNull()) { auto string = m_settings->getValue(DEFAULT_VIEW_ID).toString(); if (string.isEmpty()) { string = "Icon View"; } else { if (!m_hash->contains(string)) string = "Icon View"; } m_default_view_id_cache = string; } return m_default_view_id_cache; } const QString DirectoryViewFactoryManager2::getDefaultViewId(int zoomLevel, const QString &uri) { auto factorys = m_hash->values(); auto defaultFactory = getFactory(getDefaultViewId()); int priorty = 0; for (auto factory : factorys) { if (factory->supportUri(uri)) { if (factory->priority(uri) > priorty) { defaultFactory = factory; priorty = factory->priority(uri); continue; } } } if (!defaultFactory->supportZoom()) return defaultFactory->viewIdentity(); if (zoomLevel < 0) return getDefaultViewId(uri); if (defaultFactory->supportZoom()) { if (zoomLevel <= 20 && zoomLevel >=0) { defaultFactory = getFactory("List View"); } if (zoomLevel >= 21) { defaultFactory = getFactory("Icon View"); } } return defaultFactory->viewIdentity(); } void DirectoryViewFactoryManager2::setDefaultViewId(const QString &viewId) { if (!m_internal_views.contains(viewId)) return; if (getFactoryNames().contains(viewId)) { m_default_view_id_cache = viewId; saveDefaultViewOption(); } } void DirectoryViewFactoryManager2::saveDefaultViewOption() { m_settings->setValue(DEFAULT_VIEW_ID, m_default_view_id_cache); } peony/libpeony-qt/controls/directory-view/directory-view-factory/directory-view-factory-manager.h0000644000175000017500000000406514205101223032530 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef DIRECTORYVIEWFACTORYMANAGER_H #define DIRECTORYVIEWFACTORYMANAGER_H #include #include #include #include namespace Peony { class DirectoryViewIface; class DirectoryViewPluginIface; class DirectoryViewPluginIface2; class DirectoryViewWidget; class GlobalSettings; class PEONYCORESHARED_EXPORT DirectoryViewFactoryManager2 : public QObject { Q_OBJECT public: static DirectoryViewFactoryManager2 *getInstance(); void registerFactory(const QString &name, DirectoryViewPluginIface2 *factory); QStringList getFactoryNames(); DirectoryViewPluginIface2 *getFactory(const QString &name); const QString getDefaultViewId(const QString &uri = nullptr); const QString getDefaultViewId(int zoomLevel, const QString &uri = nullptr); const QStringList internalViews() {return m_internal_views;} public Q_SLOTS: void setDefaultViewId(const QString &viewId); void saveDefaultViewOption(); private: QHash *m_hash = nullptr; explicit DirectoryViewFactoryManager2(QObject *parent = nullptr); ~DirectoryViewFactoryManager2(); GlobalSettings *m_settings; QString m_default_view_id_cache; QStringList m_internal_views; }; } #endif // DIRECTORYVIEWFACTORYMANAGER_H peony/libpeony-qt/controls/directory-view/directory-view-factory/directory-view-factory.pri0000644000175000017500000000041514205101223031456 0ustar fengfengINCLUDEPATH += $$PWD HEADERS += \ $$PWD/icon-view-factory.h \ $$PWD/directory-view-factory-manager.h \ $$PWD/list-view-factory.h SOURCES += \ $$PWD/icon-view-factory.cpp \ $$PWD/directory-view-factory-manager.cpp \ $$PWD/list-view-factory.cpp peony/libpeony-qt/controls/directory-view/directory-view-factory/list-view-factory.cpp0000644000175000017500000000331614205101223030420 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "list-view-factory.h" #include "list-view.h" using namespace Peony; static ListViewFactory *globalInstance = nullptr; static ListViewFactory2 *globalInstance2 = nullptr; ListViewFactory *ListViewFactory::getInstance() { if (!globalInstance) { globalInstance = new ListViewFactory; } return globalInstance; } ListViewFactory::ListViewFactory(QObject *parent) : QObject (parent) { } ListViewFactory::~ListViewFactory() { } DirectoryViewIface *ListViewFactory::create() { return new Peony::DirectoryView::ListView; } //List View 2 ListViewFactory2 *ListViewFactory2::getInstance() { if (!globalInstance2) { globalInstance2 = new ListViewFactory2; } return globalInstance2; } ListViewFactory2::ListViewFactory2(QObject *parent) : QObject (parent) { } ListViewFactory2::~ListViewFactory2() { } DirectoryViewWidget *ListViewFactory2::create() { return new Peony::DirectoryView::ListView2; } peony/libpeony-qt/controls/directory-view/directory-view-factory/icon-view-factory.cpp0000644000175000017500000000331314205101223030372 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "icon-view-factory.h" #include "icon-view.h" using namespace Peony; static IconViewFactory *globalInstance = nullptr; static IconViewFactory2 *globalInstance2 = nullptr; IconViewFactory *IconViewFactory::getInstance() { if (!globalInstance) { globalInstance = new IconViewFactory; } return globalInstance; } IconViewFactory::IconViewFactory(QObject *parent) : QObject (parent) { } IconViewFactory::~IconViewFactory() { } DirectoryViewIface *IconViewFactory::create() { return new Peony::DirectoryView::IconView; } //Factory2 IconViewFactory2 *IconViewFactory2::getInstance() { if (!globalInstance2) { globalInstance2 = new IconViewFactory2; } return globalInstance2; } IconViewFactory2::IconViewFactory2(QObject *parent) : QObject (parent) { } IconViewFactory2::~IconViewFactory2() { } DirectoryViewWidget *IconViewFactory2::create() { return new Peony::DirectoryView::IconView2; } peony/libpeony-qt/controls/directory-view/directory-view-factory/list-view-factory.h0000644000175000017500000000762714205115226030106 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef LISTVIEWFACTORY_H #define LISTVIEWFACTORY_H #include #include "peony-core_global.h" #include "directory-view-plugin-iface.h" #include "directory-view-plugin-iface2.h" namespace Peony { class PEONYCORESHARED_EXPORT ListViewFactory : public QObject, public DirectoryViewPluginIface { Q_OBJECT public: static ListViewFactory* getInstance(); //plugin implement const QString name() override { return QObject::tr("List View"); } PluginType pluginType() override { return PluginType::DirectoryViewPlugin; } const QString description() override { return QObject::tr("Show the folder children as rows in a list."); } const QIcon icon() override { return QIcon::fromTheme("view-list-symbolic", QIcon::fromTheme("folder")); } void setEnable(bool enable) override { Q_UNUSED(enable) } bool isEnable() override { return true; } //directory view plugin implemeny QString viewIdentity() override { return QObject::tr("List View"); } QIcon viewIcon() override { return QIcon::fromTheme("view-list-symbolic", QIcon::fromTheme("folder")); } bool supportUri(const QString &uri) override { return !uri.isEmpty(); } DirectoryViewIface *create() override; int zoom_level_hint() override { return 20; } int priority(const QString &) override { return 0; } private: explicit ListViewFactory(QObject *parent = nullptr); ~ListViewFactory() override; }; class PEONYCORESHARED_EXPORT ListViewFactory2 : public QObject, public DirectoryViewPluginIface2 { Q_OBJECT public: static ListViewFactory2* getInstance(); //plugin implement const QString name() override { return QObject::tr("List View"); } PluginType pluginType() override { return PluginType::DirectoryViewPlugin; } const QString description() override { return QObject::tr("Show the folder children as rows in a list."); } const QIcon icon() override { return QIcon::fromTheme("view-list-symbolic", QIcon::fromTheme("folder")); } void setEnable(bool enable) override { Q_UNUSED(enable) } bool isEnable() override { return true; } //directory view plugin implemeny QString viewIdentity() override { return "List View"; } QString viewName() override { return name(); } QIcon viewIcon() override { return QIcon::fromTheme("view-list-symbolic", QIcon::fromTheme("folder")); } bool supportUri(const QString &uri) override { return !uri.isEmpty(); } DirectoryViewWidget *create() override; int zoom_level_hint() override { return 20; } int minimumSupportedZoomLevel() override { return 0; } int maximumSupportedZoomLevel() override { return 20; } int priority(const QString &) override { return 0; } bool supportZoom() override { return true; } private: explicit ListViewFactory2(QObject *parent = nullptr); ~ListViewFactory2() override; }; } #endif // LISTVIEWFACTORY_H peony/libpeony-qt/controls/directory-view/directory-view-factory/icon-view-factory.h0000644000175000017500000000747214205115226030061 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef ICONVIEWFACTORY_H #define ICONVIEWFACTORY_H #include "directory-view-plugin-iface.h" #include "directory-view-plugin-iface2.h" #include namespace Peony { class IconViewFactory : public QObject, public DirectoryViewPluginIface { Q_OBJECT public: static IconViewFactory *getInstance(); //plugin implement const QString name() override { return QObject::tr("Icon View"); } PluginType pluginType() override { return PluginType::DirectoryViewPlugin; } const QString description() override { return QObject::tr("Show the folder children as icons."); } const QIcon icon() override { return QIcon::fromTheme("view-grid-symbolic", QIcon::fromTheme("folder")); } void setEnable(bool enable) override { Q_UNUSED(enable) } bool isEnable() override { return true; } //directory view plugin implemeny QString viewIdentity() override { return QObject::tr("Icon View"); } QIcon viewIcon() override { return QIcon::fromTheme("view-grid-symbolic", QIcon::fromTheme("folder")); } bool supportUri(const QString &uri) override { return !uri.isEmpty(); } DirectoryViewIface *create() override; int zoom_level_hint() override { return 100; } int priority(const QString &) override { return 0; } private: explicit IconViewFactory(QObject *parent = nullptr); ~IconViewFactory() override; }; class IconViewFactory2 : public QObject, public DirectoryViewPluginIface2 { Q_OBJECT public: static IconViewFactory2 *getInstance(); //plugin implement const QString name() override { return QObject::tr("Icon View"); } PluginType pluginType() override { return PluginType::DirectoryViewPlugin; } const QString description() override { return QObject::tr("Show the folder children as icons."); } const QIcon icon() override { return QIcon::fromTheme("view-grid-symbolic", QIcon::fromTheme("folder")); } void setEnable(bool enable) override { Q_UNUSED(enable) } bool isEnable() override { return true; } //directory view plugin implemeny QString viewIdentity() override { return "Icon View"; } QString viewName() override { return name(); } QIcon viewIcon() override { return QIcon::fromTheme("view-grid-symbolic", QIcon::fromTheme("folder")); } bool supportUri(const QString &uri) override { return !uri.isEmpty(); } DirectoryViewWidget *create() override; int zoom_level_hint() override { return 25; } int minimumSupportedZoomLevel() override { return 21; } int maximumSupportedZoomLevel() override { return 100; } int priority(const QString &) override { return 0; } bool supportZoom() override { return true; } private: explicit IconViewFactory2(QObject *parent = nullptr); ~IconViewFactory2() override; }; } #endif // ICONVIEWFACTORY_H peony/libpeony-qt/controls/status-bar/0000755000175000017500000000000014205101223017011 5ustar fengfengpeony/libpeony-qt/controls/status-bar/status-bar.cpp0000644000175000017500000001066614205101223021613 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "status-bar.h" #include "fm-window.h" #include "file-info.h" #include #include #include #include #include using namespace Peony; StatusBar::StatusBar(FMWindowIface *window, QWidget *parent) : QStatusBar(parent) { m_styled_toolbar = new QToolBar; setContentsMargins(0, 0, 0, 0); setStyleSheet("padding: 0;"); setSizeGripEnabled(false); setMinimumHeight(30); m_window = window; m_label = new QLabel(this); m_label->setContentsMargins(0, 0, 0, 0); m_label->setWordWrap(false); m_label->setAlignment(Qt::AlignCenter); addWidget(m_label, 1); //setStyleSheet("align: center;"); //showMessage(tr("Status Bar")); } StatusBar::~StatusBar() { m_styled_toolbar->deleteLater(); } void StatusBar::update() { if (!m_window) return; auto selections = m_window->getCurrentSelectionFileInfos(); if (!selections.isEmpty()) { QString directoriesString; int directoryCount = 0; QString filesString; int fileCount = 0; goffset size = 0; for (auto selection : selections) { if(selection->isDir()) { directoryCount++; } else if (!selection->isVolume()) { fileCount++; size += selection->size(); } } //auto format_size = g_format_size(size); //Calculated by 1024 bytes auto format_size_GIB = g_format_size_full(size,G_FORMAT_SIZE_IEC_UNITS); QString format_size(format_size_GIB); //根据设计要求,按照1024字节对数据进行格式化(1GB = 1024MB),同时将GiB改为GB显示,以便于用户理解。参考windows显示样式。 format_size.replace("iB", "B"); if (selections.count() == 1) { if (directoryCount == 1) directoriesString = QString(", %1").arg(selections.first()->displayName()); if (fileCount == 1) filesString = QString(", %1, %2").arg(selections.first()->displayName()).arg(format_size); } else if (directoryCount > 1 && (fileCount > 1)) { directoriesString = tr("; %1 folders").arg(directoryCount); filesString = tr("; %1 files, %2 total").arg(fileCount).arg(format_size); } else if (directoryCount > 1 && (fileCount > 1)) { directoriesString = tr("; %1 folder").arg(directoryCount); filesString = tr("; %1 file, %2").arg(fileCount).arg(format_size); } else if (fileCount == 0) { directoriesString = tr("; %1 folders").arg(directoryCount); } else { filesString = tr("; %1 files, %2 total").arg(fileCount).arg(format_size); } m_label->setText(tr("%1 selected").arg(selections.count()) + directoriesString + filesString); //showMessage(tr("%1 files selected ").arg(selections.count())); g_free(format_size_GIB); } else { m_label->setText(m_window->getCurrentUri()); //showMessage(m_window->getCurrentUri()); } } void StatusBar::update(const QString &message) { m_label->setText(message); } void StatusBar::paintEvent(QPaintEvent *e) { //I do not want status bar draw the inserted widget's //'border', so I use painter overwrite it. QStatusBar::paintEvent(e); QPainter p(this); auto rect = this->rect(); //rect.adjust(0, 2, 0, 0); auto bg = m_styled_toolbar->palette().window().color(); p.fillRect(rect, bg); auto base = m_styled_toolbar->palette().base().color(); base.setAlpha(0); p.setCompositionMode(QPainter::CompositionMode_SourceIn); //p.fillRect(this->rect(), this->palette().base()); p.fillRect(rect, base); } peony/libpeony-qt/controls/status-bar/status-bar.pri0000644000175000017500000000014114205101223021606 0ustar fengfengINCLUDEPATH += $$PWD HEADERS += \ $$PWD/status-bar.h SOURCES += \ $$PWD/status-bar.cpp peony/libpeony-qt/controls/status-bar/status-bar.h0000644000175000017500000000257014205101223021253 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef STATUSBAR_H #define STATUSBAR_H #include #include "peony-core_global.h" class QLabel; class QToolBar; namespace Peony { class FMWindowIface; class StatusBar : public QStatusBar { Q_OBJECT public: explicit StatusBar(FMWindowIface *window, QWidget *parent = nullptr); ~StatusBar() override; public Q_SLOTS: void update(); void update(const QString &message); protected: void paintEvent(QPaintEvent *e) override; private: FMWindowIface *m_window = nullptr; QLabel *m_label = nullptr; QToolBar *m_styled_toolbar = nullptr; }; } #endif // STATUSBAR_H peony/libpeony-qt/controls/icon-container.h0000644000175000017500000000273714205101223020016 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef ICONCONTAINER_H #define ICONCONTAINER_H #include #include namespace Peony { class IconContainer : public QPushButton { Q_OBJECT public: explicit IconContainer(QWidget *parent = nullptr); ~IconContainer(); protected: void mouseMoveEvent(QMouseEvent *e) {} void mousePressEvent(QMouseEvent *e) {} void paintEvent(QPaintEvent *e); private: QStyle *m_style; }; class IconContainerStyle : public QProxyStyle { friend class IconContainer; explicit IconContainerStyle(); void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const override; }; #endif // ICONCONTAINER_H } peony/libpeony-qt/controls/preview-page/0000755000175000017500000000000014205101223017317 5ustar fengfengpeony/libpeony-qt/controls/preview-page/preview-page-factory/0000755000175000017500000000000014205101223023357 5ustar fengfengpeony/libpeony-qt/controls/preview-page/preview-page-factory/preview-page-factory-manager.cpp0000644000175000017500000000460214205101223031535 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "preview-page-factory-manager.h" #include "default-preview-page-factory.h" using namespace Peony; static PreviewPageFactoryManager *globalInstance = nullptr; PreviewPageFactoryManager *PreviewPageFactoryManager::getInstance() { if (!globalInstance) { globalInstance = new PreviewPageFactoryManager; } return globalInstance; } PreviewPageFactoryManager::PreviewPageFactoryManager(QObject *parent) : QObject(parent) { m_map = new QMap(); //load default and plugins. auto defaultFactory = DefaultPreviewPageFactory::getInstance(); registerFactory(defaultFactory->name(), static_cast(defaultFactory)); //registerFactory("test", static_cast(defaultFactory)); } PreviewPageFactoryManager::~PreviewPageFactoryManager() { if (m_map) { //FIXME: unload all module? delete m_map; } } const QStringList PreviewPageFactoryManager::getPluginNames() { QStringList l; for (auto key : m_map->keys()) { l<value(name)) { return false; } m_map->insert(name, plugin); return true; } PreviewPagePluginIface *PreviewPageFactoryManager::getPlugin(const QString &name) { m_last_preview_page_id = name; return m_map->value(name); } const QString PreviewPageFactoryManager::getLastPreviewPageId() { if (m_last_preview_page_id.isNull()) { return m_map->firstKey(); } return m_last_preview_page_id; } peony/libpeony-qt/controls/preview-page/preview-page-factory/preview-page-factory-manager.h0000644000175000017500000000450314205101223031202 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef PREVIEWPAGEFACTORYMANAGER_H #define PREVIEWPAGEFACTORYMANAGER_H #include "peony-core_global.h" #include #include namespace Peony { class PreviewPagePluginIface; /*! * \brief The PreviewPageFactoryManager class * \details * In peony-qt, we have a optional extra preview view at * right of the window. * PreviewPage is provided by an inplement of PreviewPagePluginInterface. * Actually, the interface is represent a factory template for * create a preview page. * We use this class manage all the factories of preview page. * When the manger instance init, the factories will be loaded * and registered in manager. Then we can call the createPreviewPage() * function of them when we need. The registered factory can be * listed in a GUI, and we can disable/enable them thourgh the * setEnable() method. * \note * The manager is single and global, and factory too. * Do not try newing or deleting them. */ class PEONYCORESHARED_EXPORT PreviewPageFactoryManager : public QObject { Q_OBJECT public: static PreviewPageFactoryManager *getInstance(); bool registerFactory(const QString &name, PreviewPagePluginIface* plugin); const QStringList getPluginNames(); PreviewPagePluginIface *getPlugin(const QString &name); const QString getLastPreviewPageId(); private: explicit PreviewPageFactoryManager(QObject *parent = nullptr); ~PreviewPageFactoryManager(); QMap *m_map = nullptr; QString m_last_preview_page_id = nullptr; }; } #endif // PREVIEWPAGEFACTORYMANAGER_H peony/libpeony-qt/controls/preview-page/preview-page-factory/preview-page-factory.pri0000644000175000017500000000020514205101223030130 0ustar fengfengINCLUDEPATH += $$PWD HEADERS += \ $$PWD/preview-page-factory-manager.h SOURCES += \ $$PWD/preview-page-factory-manager.cpp peony/libpeony-qt/controls/preview-page/preview-page.pri0000644000175000017500000000020414205101223022422 0ustar fengfengINCLUDEPATH += $$PWD include(preview-page-factory/preview-page-factory.pri) include(default-preview-page/default-preview-page.pri) peony/libpeony-qt/controls/preview-page/default-preview-page/0000755000175000017500000000000014205115226023344 5ustar fengfengpeony/libpeony-qt/controls/preview-page/default-preview-page/default-preview-page-factory.cpp0000644000175000017500000000256514205101223031532 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "default-preview-page-factory.h" #include "default-preview-page.h" using namespace Peony; static DefaultPreviewPageFactory *globalInstance = nullptr; DefaultPreviewPageFactory *DefaultPreviewPageFactory::getInstance() { if (!globalInstance) { globalInstance = new DefaultPreviewPageFactory; } return globalInstance; } DefaultPreviewPageFactory::DefaultPreviewPageFactory(QObject *parent) : QObject(parent) { } DefaultPreviewPageFactory::~DefaultPreviewPageFactory() { } PreviewPageIface *DefaultPreviewPageFactory::createPreviewPage() { return new DefaultPreviewPage; } peony/libpeony-qt/controls/preview-page/default-preview-page/default-preview-page-factory.h0000644000175000017500000000424414205101223031173 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef DEFAULTPREVIEWPAGEFACTORY_H #define DEFAULTPREVIEWPAGEFACTORY_H #include "peony-core_global.h" #include "preview-page-plugin-iface.h" #include namespace Peony { /*! * \brief The DefaultPreviewPageFactory class * \details * This class is used to create the preview page of peony-qt. * \note * This is a interanl interface implement, so it is not a real plugin. * The interface implement is not so strict than a plugin one. */ class PEONYCORESHARED_EXPORT DefaultPreviewPageFactory : public QObject, public PreviewPagePluginIface { Q_OBJECT public: static DefaultPreviewPageFactory *getInstance(); PluginType pluginType() override { return PluginType::PreviewPagePlugin; } const QString name() override { return tr("Default Preview"); } const QString description() override { return tr("This is the Default Preview of peony-qt"); } const QIcon icon() override { return QIcon::fromTheme("ukui-preview-file", QIcon::fromTheme("preview-file")); } void setEnable(bool enable) override { m_enable = enable; } bool isEnable() override { return m_enable; } PreviewPageIface *createPreviewPage() override; private: explicit DefaultPreviewPageFactory(QObject *parent = nullptr); ~DefaultPreviewPageFactory() override; bool m_enable = true; }; } #endif // DEFAULTPREVIEWPAGEFACTORY_H peony/libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.cpp0000644000175000017500000002721614205115226030075 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "default-preview-page.h" #include "thumbnail-manager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "icon-container.h" #include "file-info.h" #include "file-info-manager.h" #include "file-watcher.h" #include "file-info-job.h" #include "file-count-operation.h" using namespace Peony; #define LABEL_MAX_WIDTH 165 DefaultPreviewPage::DefaultPreviewPage(QWidget *parent) : QStackedWidget (parent) { setContentsMargins(10, 20, 10, 20); 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; /* label = new QLabel(this); label->setWordWrap(true); label->setAlignment(Qt::AlignCenter); */ auto previewPage = new FilePreviewPage(this); previewPage->installEventFilter(this); m_preview_tab_widget = previewPage; addWidget(m_preview_tab_widget); addWidget(m_empty_tab_widget); setCurrentWidget(m_empty_tab_widget); if (QGSettings::isSchemaInstalled("org.ukui.style")) { QGSettings *gSetting = new QGSettings("org.ukui.style", QByteArray(), this); connect(gSetting, &QGSettings::changed, this, [=](const QString &key) { if ("systemFontSize" == key) { if (m_support && m_preview_tab_widget) { if (m_info) { m_preview_tab_widget->updateInfo(m_info.get()); } } } }); } } DefaultPreviewPage::~DefaultPreviewPage() { cancel(); } bool DefaultPreviewPage::eventFilter(QObject *obj, QEvent *ev) { if (obj == m_preview_tab_widget) { if (ev->type() == QEvent::Resize) { auto e = static_cast(ev); auto page = qobject_cast(m_preview_tab_widget); int width = e->size().width() - 50; width = qMax(width, 96); width = qMin(width, 256); page->resizeIcon(QSize(width, width * 2/3)); } } return QStackedWidget::eventFilter(obj, ev); } void DefaultPreviewPage::prepare(const QString &uri, PreviewType type) { m_current_uri = uri; m_info = FileInfo::fromUri(uri); m_current_type = type; m_support = uri.contains("file:///"); m_watcher = std::make_shared(uri); connect(m_watcher.get(), &FileWatcher::locationChanged, [=](const QString &, const QString &newUri) { this->prepare(newUri); this->startPreview(); }); m_watcher->startMonitor(); } void DefaultPreviewPage::prepare(const QString &uri) { prepare(uri, Other); } void DefaultPreviewPage::startPreview() { if (m_support) { auto previewPage = qobject_cast(m_preview_tab_widget); auto info = FileInfo::fromUri(m_current_uri); previewPage->updateInfo(m_info.get()); setCurrentWidget(previewPage); } else { QLabel *label = qobject_cast(m_empty_tab_widget); label->setText(tr("Can not preview this file.")); } } void DefaultPreviewPage::cancel() { m_preview_tab_widget->cancel(); setCurrentWidget(m_empty_tab_widget); QLabel *label = qobject_cast(m_empty_tab_widget); label->setText(tr("Select the file you want to preview...")); } void DefaultPreviewPage::closePreviewPage() { cancel(); deleteLater(); } void DefaultPreviewPage::paintEvent(QPaintEvent *e) { QPainter p(this); p.setCompositionMode(QPainter::CompositionMode_SourceIn); p.fillRect(this->rect(), this->palette().base()); QStackedWidget::paintEvent(e); } FilePreviewPage::FilePreviewPage(QWidget *parent) : QFrame(parent) { m_layout = new QGridLayout(this); setLayout(m_layout); m_icon = new IconContainer(this); m_icon->setIconSize(QSize(96, 96)); m_layout->addWidget(m_icon); m_form = new QFormLayout(this); m_form->setSpacing(3); m_display_name_label = new QLabel(this); QLabel *file_name_label = new QLabel(this); file_name_label->setAlignment(Qt::AlignTop); file_name_label->setText(tr("File Name:")); m_form->addRow(file_name_label, m_display_name_label); m_type_label = new QLabel(this); m_form->addRow(tr("File Type:"), m_type_label); m_time_access_label = new QLabel(this); m_form->addRow(tr("Time Access:"), m_time_access_label); m_time_modified_label = new QLabel(this); m_form->addRow(tr("Time Modified:"), m_time_modified_label); m_file_count_label = new QLabel(this); QLabel *children_label = new QLabel(this); children_label->setAlignment(Qt::AlignTop); children_label->setText(tr("Children Count:")); m_form->addRow(children_label, m_file_count_label); m_total_size_label = new QLabel(this); m_form->addRow(tr("Size:"), m_total_size_label); //image m_image_size = new QLabel(this); m_form->addRow(tr("Image resolution:"), m_image_size); m_image_format = new QLabel(this); m_form->addRow(tr("color model:"), m_image_format); m_form->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint); m_form->setFormAlignment(Qt::AlignHCenter); m_form->setLabelAlignment(Qt::AlignLeft); QWidget *form = new QWidget(this); form->setLayout(m_form); m_layout->addWidget(form, 1, 0); } FilePreviewPage::~FilePreviewPage() { } void FilePreviewPage::wrapData(QLabel *p_label, const QString &text) { QString wrapText = text; QFontMetrics fontMetrics = p_label->fontMetrics(); int textSize = fontMetrics.width(wrapText); if(textSize > LABEL_MAX_WIDTH){ int lastIndex = 0; for(int i = lastIndex; i < wrapText.length(); i++) { if(fontMetrics.width(wrapText.mid(lastIndex, i - lastIndex)) == LABEL_MAX_WIDTH) { lastIndex = i; wrapText.insert(i, '\n'); } else if(fontMetrics.width(wrapText.mid(lastIndex, i - lastIndex)) > LABEL_MAX_WIDTH) { lastIndex = i; wrapText.insert(i - 1, '\n'); } else { continue; } } } p_label->setText(wrapText); } void FilePreviewPage::updateInfo(FileInfo *info) { if (info->displayName().isEmpty()) { FileInfoJob j(info->uri()); j.querySync(); } auto thumbnail = ThumbnailManager::getInstance()->tryGetThumbnail(info->uri()); if (!thumbnail.isNull()) { QUrl url = info->uri(); thumbnail.addFile(url.path()); } auto icon = QIcon::fromTheme(info->iconName(), QIcon::fromTheme("text-x-generic")); m_icon->setIcon(thumbnail.isNull()? icon: thumbnail); wrapData(m_display_name_label, info->displayName()); wrapData(m_type_label, info->fileType()); auto access = QDateTime::fromMSecsSinceEpoch(info->accessTime()*1000); auto modify = QDateTime::fromMSecsSinceEpoch(info->modifiedTime()*1000); wrapData(m_time_access_label, access.toString(Qt::SystemLocaleShortDate)); wrapData(m_time_modified_label, modify.toString(Qt::SystemLocaleShortDate)); m_file_count_label->setText(tr("")); if (info->isDir()) { m_form->itemAt(4, QFormLayout::LabelRole)->widget()->setVisible(true); m_file_count_label->setVisible(true); } else { m_form->itemAt(4, QFormLayout::LabelRole)->widget()->setVisible(false); m_file_count_label->setVisible(false); } if (info->mimeType().startsWith("image/")) { QUrl url = info->uri(); QImageReader r(url.path()); auto image_size_row_left = m_form->itemAt(6, QFormLayout::LabelRole)->widget(); image_size_row_left->setVisible(true); m_image_size->setText(tr("%1x%2").arg(r.size().width()).arg(r.size().height())); auto thumbnail = ThumbnailManager::getInstance()->tryGetThumbnail(info->uri()); bool rgba = thumbnail.pixmap(r.size()).hasAlphaChannel(); m_image_size->setVisible(true); auto image_format_row_left = m_form->itemAt(7, QFormLayout::LabelRole)->widget(); image_format_row_left->setVisible(true); m_image_format->setText(rgba? "RGBA": "RGB"); m_image_format->setVisible(true); } else { auto image_size_row_left = m_form->itemAt(6, QFormLayout::LabelRole)->widget(); image_size_row_left->setVisible(false); m_image_size->setVisible(false); auto image_format_row_left = m_form->itemAt(7, QFormLayout::LabelRole)->widget(); image_format_row_left->setVisible(false); m_image_format->setVisible(false); } if (info->fileType().startsWith("video/")) { } if (info->fileType().startsWith("audio/")) { } if (info->isSymbolLink()&&!info->symlinkTarget().isEmpty()){ countAsync("file:///" + info->symlinkTarget()); } else { countAsync(info->uri()); } } void FilePreviewPage::countAsync(const QString &uri) { cancel(); m_file_count = 0; m_hidden_count = 0; m_total_size = 0; QStringList uris; uris<isDir()); connect(m_count_op, &FileOperation::operationStarted, this, &FilePreviewPage::resetCount, Qt::BlockingQueuedConnection); connect(m_count_op, &FileOperation::operationPreparedOne, this, &FilePreviewPage::onPreparedOne, Qt::BlockingQueuedConnection); connect(m_count_op, &FileCountOperation::countDone, this, &FilePreviewPage::onCountDone, Qt::BlockingQueuedConnection); QThreadPool::globalInstance()->start(m_count_op); } void FilePreviewPage::updateCount() { wrapData(m_file_count_label, tr("%1 total, %2 hidden").arg(m_file_count).arg(m_hidden_count)); auto format = g_format_size_full(m_total_size,G_FORMAT_SIZE_IEC_UNITS); QString fileSize(format); if (fileSize.contains("KiB")) { fileSize.replace("KiB", "KB"); } else if (fileSize.contains("MiB")) { fileSize.replace("MiB", "MB"); } else if (fileSize.contains("GiB")) { fileSize.replace("GiB", "GB"); } m_total_size_label->setText(fileSize); g_free(format); } void FilePreviewPage::cancel() { if (m_count_op) { m_count_op->blockSignals(true); m_count_op->cancel(); onCountDone(); } m_count_op = nullptr; } void FilePreviewPage::resizeIcon(QSize size) { m_icon->setIconSize(size); } void FilePreviewPage::resetCount() { m_file_count = 0; m_hidden_count = 0; m_total_size = 0; updateCount(); } void FilePreviewPage::onCountDone() { if (!m_count_op) return; m_count_op->getInfo(m_file_count, m_hidden_count, m_total_size); this->updateCount(); m_count_op = nullptr; m_file_count = 0; m_hidden_count = 0; m_total_size = 0; } peony/libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.pri0000644000175000017500000000031514205101223030064 0ustar fengfengINCLUDEPATH += $$PWD HEADERS += \ $$PWD/default-preview-page.h \ $$PWD/default-preview-page-factory.h SOURCES += \ $$PWD/default-preview-page.cpp \ $$PWD/default-preview-page-factory.cpp peony/libpeony-qt/controls/preview-page/default-preview-page/default-preview-page.h0000644000175000017500000000637414205101223027534 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #ifndef DEFAULTPREVIEWPAGE_H #define DEFAULTPREVIEWPAGE_H #include "peony-core_global.h" #include "preview-page-plugin-iface.h" #include #include class QGridLayout; class QPushButton; class QFormLayout; class QLabel; namespace Peony { class FileInfo; class FileWatcher; class IconContainer; class FilePreviewPage; /*! * \brief The DefaultPreviewPage class * \todo * Implement a preview page containing file thumbnail and descriptions. */ class PEONYCORESHARED_EXPORT DefaultPreviewPage : public QStackedWidget, public PreviewPageIface { Q_OBJECT public: explicit DefaultPreviewPage(QWidget *parent = nullptr); ~DefaultPreviewPage() override; void prepare(const QString &uri, PreviewType type) override; void prepare(const QString &uri) override; void startPreview() override; void cancel() override; void closePreviewPage() override; protected: bool eventFilter(QObject *obj, QEvent *ev) override; void paintEvent(QPaintEvent *e) override; private: QString m_current_uri; PreviewType m_current_type; QWidget *m_empty_tab_widget; FilePreviewPage *m_preview_tab_widget; std::shared_ptr m_info; std::shared_ptr m_watcher; bool m_support = true; }; class FileCountOperation; class FilePreviewPage : public QFrame { friend class DefaultPreviewPage; Q_OBJECT private: explicit FilePreviewPage(QWidget *parent = nullptr); ~FilePreviewPage(); public: void wrapData(QLabel *p_label, const QString &text); private Q_SLOTS: void updateInfo(FileInfo *info); void countAsync(const QString &uri); void updateCount(); void cancel(); void resizeIcon(QSize size); protected Q_SLOTS: void resetCount(); void onPreparedOne(const QString &uri, quint64 size) { m_file_count++; if (uri.contains("/.")) { m_hidden_count++; } m_total_size += size; this->updateCount(); } void onCountDone(); private: FileCountOperation *m_count_op = nullptr; quint64 m_file_count = 0; quint64 m_hidden_count = 0; quint64 m_total_size = 0; QGridLayout *m_layout; IconContainer *m_icon; QFormLayout *m_form; QLabel *m_display_name_label; QLabel *m_type_label; QLabel *m_file_count_label; QLabel *m_total_size_label; QLabel *m_time_modified_label; QLabel *m_time_access_label; //image QLabel *m_image_size; QLabel *m_image_format; }; } #endif // DEFAULTPREVIEWPAGE_H peony/libpeony-qt/controls/tool-bar/0000755000175000017500000000000014205115226016453 5ustar fengfengpeony/libpeony-qt/controls/tool-bar/search-bar-container.cpp0000644000175000017500000001164614205115226023156 0ustar fengfeng/* * Peony-Qt * * Copyright (C) 2020, 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 . * * Authors: MeihongHe * */ #include #include "search-bar-container.h" #include #include #include #include using namespace Peony; SearchBarContainer::SearchBarContainer(QWidget *parent): QWidget(parent) { QHBoxLayout *layout = new QHBoxLayout(this); this->setLayout(layout); m_layout = layout; layout->setContentsMargins(0,0,0,0); // QComboBox *filter = new QComboBox(this); // m_filter_box = filter; // filter->setToolTip(tr("Choose File Type")); auto model = new QStringListModel(this); model->setStringList(m_file_type_list); // filter->setModel(model); // filter->setFixedWidth(80); // filter->setFixedHeight(parent->height()); QLineEdit *edit = new QLineEdit(this); m_search_box = edit; edit->setFixedHeight(parent->height()); // layout->addWidget(filter, Qt::AlignLeft); layout->addWidget(edit, Qt::AlignLeft); //search history m_model = new QStringListModel(m_search_box); QCompleter *completer = new QCompleter(m_search_box); completer->setModel(m_model); completer->setMaxVisibleItems(10); auto m_list = m_model->stringList(); m_list.prepend(tr("Clear")); m_model->setStringList(m_list); m_list_view = new QListView(m_search_box); m_list_view->setAttribute(Qt::WA_TranslucentBackground); //m_list_view->viewport()->setAttribute(Qt::WA_TranslucentBackground); m_list_view->setProperty("useCustomShadow", true); m_list_view->setProperty("customShadowDarkness", 0.5); m_list_view->setProperty("customShadowWidth", 20); m_list_view->setProperty("customShadowRadius", QVector4D(6, 6, 6, 6)); m_list_view->setProperty("customShadowMargins", QVector4D(20, 20, 20, 20)); m_list_view->setModel(m_model); completer->setPopup(m_list_view); //change QCompleter Mode form UnfilteredPopupCompletion to PopupCompletion //to fix can not input chinese continuous issue completer->setCompletionMode(QCompleter::PopupCompletion); m_search_box->setCompleter(completer); m_search_trigger.setInterval(500); m_clear_action = true; connect(&m_search_trigger, SIGNAL(timeout()), this, SLOT(startSearch())); connect(m_search_box, &QLineEdit::textChanged, [=]() { //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; }); // connect(m_filter_box, &QComboBox::currentTextChanged, [=]() // { // Q_EMIT this->filterUpdate(m_filter_box->currentIndex()); // }); QAction *searchAction = m_search_box->addAction(QIcon::fromTheme("ukui-down-symbolic", QIcon(":/icons/ukui-down-symbolic")), QLineEdit::TrailingPosition); searchAction->setProperty("isWindowButton", 1); searchAction->setProperty("useIconHighlightEffect", 2); searchAction->setProperty("isIcon", true); connect(searchAction, &QAction::triggered, this, [=]() { //qDebug() << "triggered search history!"; m_search_box->completer()->complete(); }); connect(m_list_view, SIGNAL(clicked(const QModelIndex &)), this, SLOT(onTableClicked(const QModelIndex &))); } QSize SearchBarContainer::sizeHint() const { return this->topLevelWidget()->sizeHint(); } void SearchBarContainer::onTableClicked(const QModelIndex &index) { //qDebug() << "onTableClicked:" <rowCount(); if (index.row() != m_model->rowCount()-1) { m_search_box->setText(index.data().toString()); return; } auto l = m_model->stringList(); l.clear(); l.prepend(tr("Clear")); m_model->setStringList(l); m_search_box->setText(""); } void SearchBarContainer::startSearch() { auto l = m_model->stringList(); if (! l.contains(m_search_box->text())) l.prepend(m_search_box->text()); //qDebug() << "SearchBarContainer::startSearch:" <text(); m_model->setStringList(l); Q_EMIT this->returnPressed(); } void SearchBarContainer::clearSearchBox() { m_search_box->setText(""); m_clear_action = true; //need stop search action m_search_trigger.stop(); } peony/libpeony-qt/controls/tool-bar/tool-bar.cpp0000644000175000017500000003362014205115226020702 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "tool-bar.h" #include "search-bar.h" #include "fm-window.h" #include "directory-view-factory-manager.h" #include "directory-view-plugin-iface.h" #include "directory-view-plugin-iface2.h" #include "directory-view-widget.h" #include "clipboard-utils.h" #include "file-operation-utils.h" #include "view-factory-model.h" #include "view-factory-sort-filter-model.h" #include "directory-view-container.h" #include "global-settings.h" #include "audio-play-manager.h" #include #include #include #include #include #include #include #include #include #include using namespace Peony; ToolBar::ToolBar(FMWindow *window, QWidget *parent) : QToolBar(parent) { setContentsMargins(0, 0, 0, 0); setFixedHeight(50); m_top_window = window; init(); } void ToolBar::init() { //layout QAction *newWindowAction = addAction(QIcon::fromTheme("window-new-symbolic"), tr("Open in New window")); QAction *newTabActon = addAction(QIcon::fromTheme("tab-new-symbolic"), tr("Open in new Tab")); addSeparator(); //view switch //FIXME: how about support uri? auto viewManager = DirectoryViewFactoryManager2::getInstance(); auto defaultViewId = viewManager->getDefaultViewId(); auto model = new ViewFactorySortFilterModel2(this); m_view_factory_model = model; model->setDirectoryUri("file://" + QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); /* QComboBox *viewCombox = new QComboBox(this); m_view_option_box = viewCombox; viewCombox->setToolTip(tr("Change Directory View")); viewCombox->setModel(model); addWidget(viewCombox); connect(viewCombox, QOverload::of(&QComboBox::currentIndexChanged), [=](int index){ auto viewId = viewCombox->itemData(index, Qt::ToolTipRole).toString(); m_top_window->beginSwitchView(viewId); }); addSeparator(); */ m_view_action = new QAction(m_view_factory_model->iconFromViewId(m_top_window->getCurrentPageViewType()), m_top_window->getCurrentPageViewType(), this); m_view_menu = new QMenu(this); m_view_action->setMenu(m_view_menu); connect(m_view_action, &QAction::triggered, [=]() { auto point = this->widgetForAction(m_view_action)->geometry().bottomLeft(); auto global_point = mapToGlobal(point); m_view_menu->exec(global_point); }); connect(m_view_menu, &QMenu::aboutToShow, [=]() { for (auto id : m_view_factory_model->supportViewIds()) { auto action = m_view_menu->addAction(m_view_factory_model->iconFromViewId(id), id, [=]() { m_top_window->getCurrentPage()->switchViewType(id); }); if (id == m_top_window->getCurrentPageViewType()) { action->setCheckable(true); action->setChecked(true); } } }); connect(m_view_menu, &QMenu::aboutToHide, [=]() { for (auto action : m_view_menu->actions()) { action->deleteLater(); } }); addAction(m_view_action); addSeparator(); //sort type /*! \todo make column extensionable. */ m_sort_action = new QAction(QIcon::fromTheme("view-sort-ascending-symbolic"), tr("Sort Type"), this); QMenu *sortMenu = new QMenu(this); sortMenu->addAction(tr("File Name"), [=]() { m_top_window->setCurrentSortColumn(0); }); sortMenu->addAction(tr("Modified Date"), [=]() { m_top_window->setCurrentSortColumn(1); }); sortMenu->addAction(tr("File Type"), [=]() { m_top_window->setCurrentSortColumn(2); }); sortMenu->addAction(tr("File Size"), [=]() { m_top_window->setCurrentSortColumn(3); }); sortMenu->addSeparator(); sortMenu->addAction(tr("Ascending"), [=]() { m_top_window->setCurrentSortOrder(Qt::AscendingOrder); m_sort_action->setIcon(QIcon::fromTheme("view-sort-ascending-symbolic")); }); sortMenu->addAction(tr("Descending"), [=] { m_top_window->setCurrentSortOrder(Qt::DescendingOrder); m_sort_action->setIcon(QIcon::fromTheme("view-sort-descending-symbolic")); }); m_sort_action->setMenu(sortMenu); addAction(m_sort_action); connect(m_sort_action, &QAction::triggered, [=]() { auto point = this->widgetForAction(m_sort_action)->geometry().bottomLeft(); auto global_point = mapToGlobal(point); sortMenu->exec(global_point); }); connect(sortMenu, &QMenu::aboutToShow, [=]() { for (auto action : sortMenu->actions()) { action->setCheckable(false); action->setChecked(false); } int column = m_top_window->getCurrentSortColumn(); int order = m_top_window->getCurrentSortOrder(); sortMenu->actions().at(column)->setCheckable(true); sortMenu->actions().at(column)->setChecked(true); sortMenu->actions().at(order + 5)->setCheckable(true); sortMenu->actions().at(order + 5)->setChecked(true); }); addSeparator(); //file operations QAction *copyAction = addAction(QIcon::fromTheme("edit-copy-symbolic"), tr("Copy")); copyAction->setShortcut(QKeySequence::Copy); QAction *pasteAction = addAction(QIcon::fromTheme("edit-paste-symbolic"), tr("Paste")); pasteAction->setShortcut(QKeySequence::Paste); QAction *cutAction = addAction(QIcon::fromTheme("edit-cut-symbolic"), tr("Cut")); cutAction->setShortcut(QKeySequence::Cut); QAction *trashAction = addAction(QIcon::fromTheme("edit-delete-symbolic"), tr("Trash")); //trashAction->setShortcut(QKeySequence::Delete); m_file_op_actions<playWarningAudio(); auto result = QMessageBox::question(nullptr, tr("Delete Permanently"), tr("Are you sure that you want to delete these files? " "Once you start a deletion, the files deleting will never be " "restored again.")); if (result == QMessageBox::Yes) { auto uris = m_top_window->getCurrentAllFileUris(); qDebug()<getCurrentSelections()); }); m_trash_actions_sperator = addSeparator(); //connect signal connect(newWindowAction, &QAction::triggered, [=]() { FMWindow *newWindow = new FMWindow(m_top_window->getLastNonSearchUri()); newWindow->show(); //FIXME: show when prepared }); connect(newTabActon, &QAction::triggered, [=]() { QStringList l; l<getLastNonSearchUri(); m_top_window->addNewTabs(l); }); /* connect(viewCombox, QOverload::of(&QComboBox::currentIndexChanged), [=](const QString &text){ Q_EMIT this->optionRequest(SwitchView); //FIXME: i have to add interface to view proxy for view switch. }); */ connect(copyAction, &QAction::triggered, [=]() { if (!m_top_window->getCurrentSelections().isEmpty()) ClipboardUtils::setClipboardFiles(m_top_window->getCurrentSelections(), false); }); connect(pasteAction, &QAction::triggered, [=]() { if (ClipboardUtils::isClipboardHasFiles()) { //FIXME: how about duplicated copy? //FIXME: how to deal with a failed move? ClipboardUtils::pasteClipboardFiles(m_top_window->getCurrentUri()); } }); connect(cutAction, &QAction::triggered, [=]() { if (!m_top_window->getCurrentSelections().isEmpty()) { ClipboardUtils::setClipboardFiles(m_top_window->getCurrentSelections(), true); } }); connect(trashAction, &QAction::triggered, [=]() { if (!m_top_window->getCurrentSelections().isEmpty()) { FileOperationUtils::trash(m_top_window->getCurrentSelections(), true); } }); QAction *optionAction = new QAction(QIcon::fromTheme("ukui-settings-app-symbolic", QIcon::fromTheme("settings-app-symbolic")), tr("Options"), nullptr); connect(optionAction, &QAction::triggered, this, [=]() { QMenu optionMenu; auto forbidThumbnail = optionMenu.addAction(tr("Forbid Thumbnail"), this, [=](bool checked) { GlobalSettings::getInstance()->setValue(FORBID_THUMBNAIL_IN_VIEW, checked); m_top_window->refresh(); }); forbidThumbnail->setCheckable(true); forbidThumbnail->setChecked(GlobalSettings::getInstance()->isExist(FORBID_THUMBNAIL_IN_VIEW)? GlobalSettings::getInstance()->getValue(FORBID_THUMBNAIL_IN_VIEW).toBool(): false); auto showHidden = optionMenu.addAction(tr("Show Hidden"), this, [=]() { m_top_window->setShowHidden(); }); showHidden->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_H)); showHidden->setCheckable(true); showHidden->setChecked(GlobalSettings::getInstance()->isExist(SHOW_HIDDEN_PREFERENCE)? GlobalSettings::getInstance()->getValue(SHOW_HIDDEN_PREFERENCE).toBool(): false); auto resident = optionMenu.addAction(tr("Resident in Backend")); resident->setToolTip(tr("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.")); connect(resident, &QAction::triggered, this, [=](bool checked) { GlobalSettings::getInstance()->setValue(RESIDENT_IN_BACKEND, checked); qApp->setQuitOnLastWindowClosed(!checked); }); resident->setCheckable(true); resident->setChecked(GlobalSettings::getInstance()->isExist(RESIDENT_IN_BACKEND)? GlobalSettings::getInstance()->getValue(RESIDENT_IN_BACKEND).toBool(): false); optionMenu.addSeparator(); auto help = optionMenu.addAction(QIcon::fromTheme("help-symbolic"), tr("&Help"), this, [=]() { QUrl url = QUrl("help:ubuntu-kylin-help/files", QUrl::TolerantMode); QDesktopServices::openUrl(url); }); help->setShortcut(Qt::Key_F1); auto about = optionMenu.addAction(tr("&About..."), this, [=]() { QMessageBox::about(m_top_window, tr("Peony Qt"), tr("Author:\n" "\tYue Lan \n" "\tMeihong He \n" "\n" "Copyright (C): 2019, Tianjin KYLIN Information Technology Co., Ltd.")); }); about->setShortcut(Qt::CTRL + Qt::Key_F2); auto point = this->widgetForAction(optionAction)->geometry().bottomLeft(); auto global_point = mapToGlobal(point); optionMenu.exec(global_point); }); addAction(optionAction); //extension //trash } void ToolBar::updateLocation(const QString &uri) { if (uri.isNull()) return; bool isFileOpDisable = uri.startsWith("trash://") || uri.startsWith("search://") || uri.startsWith("computer:///"); for (auto action : m_file_op_actions) { action->setEnabled(!isFileOpDisable); if (uri.startsWith("search://")) { if (action->text() == tr("Copy")) { action->setEnabled(true); } } } m_view_factory_model->setDirectoryUri(uri); auto viewId = m_top_window->getCurrentPage()->getView()->viewId(); m_view_action->setIcon(m_view_factory_model->iconFromViewId(viewId)); m_view_action->setText(m_top_window->getCurrentPageViewType()); /* auto index = m_view_factory_model->getIndexFromViewId(viewId); if (index.isValid()) m_view_option_box->setCurrentIndex(index.row()); else { m_view_option_box->setCurrentIndex(0); } */ m_clean_trash_action->setVisible(uri.startsWith("trash://")); m_restore_action->setVisible(uri.startsWith("trash://")); m_trash_actions_sperator->setVisible(uri.startsWith("trash://")); } void ToolBar::updateStates() { if (!m_top_window) return; auto directory = m_top_window->getCurrentUri(); auto selection = m_top_window->getCurrentSelections(); if (directory.startsWith("trash://")) { auto files = m_top_window->getCurrentAllFileUris(); m_clean_trash_action->setEnabled(!files.isEmpty()); m_restore_action->setEnabled(!selection.isEmpty()); } if (m_top_window->getCurrentSortOrder() == Qt::AscendingOrder) { m_sort_action->setIcon(QIcon::fromTheme("view-sort-ascending-symbolic")); } else { m_sort_action->setIcon(QIcon::fromTheme("view-sort-descending-symbolic")); } } peony/libpeony-qt/controls/tool-bar/advance-search-bar.h0000644000175000017500000000436414205101223022231 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Meihong He * */ #ifndef ADVANCE_SEARCH_BAR_H #define ADVANCE_SEARCH_BAR_H #include "advanced-location-bar.h" #include #include #include #include namespace Peony { class FMWindowIface; class AdvanceSearchBar : public QScrollArea { Q_OBJECT public: explicit AdvanceSearchBar(FMWindowIface *window, QWidget *parent = nullptr); Q_SIGNALS: public Q_SLOTS: void clearData(); void browsePath(); void searchFilter(); void filterUpdate(); void setShowHidden(); void updateLocation(); void pathChanged(); void setdefaultpath(QString path); public: //advance search filter options 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("this week"), tr("this month"), tr("this year"), tr("year ago")}; QStringList m_file_size_list = {tr("all"), tr("tiny(0-16K)"), tr("small(16k-1M)"), tr("medium(1M-100M)"), tr("big(100M-1G)"),tr("large(>1G)")}; protected: void init(); private: FMWindowIface *m_top_window; QWidget *m_filter; QLineEdit *m_advanced_key, *m_search_path; QComboBox *typeViewCombox, *timeViewCombox, *sizeViewCombox; QString m_advance_target_path, m_last_show_name; QList m_choosed_paths; bool m_search_content, m_search_name; }; } #endif // ADVANCE_SEARCH_BAR_H peony/libpeony-qt/controls/tool-bar/view-factory-sort-filter-model.cpp0000644000175000017500000000755214205101223025145 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "view-factory-sort-filter-model.h" #include "directory-view-factory-manager.h" #include "directory-view-plugin-iface.h" #include "directory-view-plugin-iface2.h" #include "view-factory-model.h" using namespace Peony; //Proxy Model 2 ViewFactorySortFilterModel2::ViewFactorySortFilterModel2(QObject *parent) : QSortFilterProxyModel(parent) { ViewFactoryModel2 *model = new ViewFactoryModel2(this); setSourceModel(model); } bool ViewFactorySortFilterModel2::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { ViewFactoryModel2 *model = static_cast(sourceModel()); auto index = model->index(sourceRow, 0, sourceParent); auto manager = DirectoryViewFactoryManager2::getInstance(); auto viewId = index.data(Qt::UserRole).toString(); auto factory = manager->getFactory(viewId); return factory->supportUri(model->m_current_uri); int zoom_level_hint = factory->zoom_level_hint(); int priority = factory->priority(model->m_current_uri); auto same_zoom_level_factories = model->m_factory_hash.values(zoom_level_hint); bool accept = true; for (auto pair : same_zoom_level_factories) { if (pair.first > priority) { accept = false; break; } } return accept; } bool ViewFactorySortFilterModel2::lessThan(const QModelIndex &left, const QModelIndex &right) const { auto manager = DirectoryViewFactoryManager2::getInstance(); auto leftId = left.data(Qt::UserRole).toString(); auto rightId = right.data(Qt::UserRole).toString(); auto leftFactory = manager->getFactory(leftId); auto rightFactory = manager->getFactory(rightId); return leftFactory->zoom_level_hint()zoom_level_hint(); } void ViewFactorySortFilterModel2::setDirectoryUri(const QString &uri) { ViewFactoryModel2 *model = static_cast(sourceModel()); model->setDirectoryUri(uri); sort(0); } const QModelIndex ViewFactorySortFilterModel2::getIndexFromViewId(const QString &viewId) { ViewFactoryModel2 *model = static_cast(sourceModel()); auto sourceIndex = model->getIndexFromViewId(viewId); return mapFromSource(sourceIndex); } const QString ViewFactorySortFilterModel2::getHighestPriorityViewId(int zoom_level_hint) { ViewFactoryModel2 *model = static_cast(sourceModel()); return model->getHighestPriorityViewId(zoom_level_hint); } const QStringList ViewFactorySortFilterModel2::supportViewIds() { //QStringList l; ViewFactoryModel2 *model = static_cast(sourceModel()); auto l = model->supportViewIds(); l.sort(); return l; //return l; } const QIcon ViewFactorySortFilterModel2::iconFromViewId(const QString &viewId) { auto manager = DirectoryViewFactoryManager2::getInstance(); auto factory = manager->getFactory(viewId); return factory->icon(); } const QString ViewFactorySortFilterModel2::getViewDisplayNameFromId(const QString &viewId) { auto manager = DirectoryViewFactoryManager2::getInstance(); return manager->getFactory(viewId)->viewName(); } peony/libpeony-qt/controls/tool-bar/view-factory-model.cpp0000644000175000017500000000712314205101223022667 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * */ #include "view-factory-model.h" #include "directory-view-factory-manager.h" #include "directory-view-plugin-iface.h" #include "directory-view-plugin-iface2.h" #include using namespace Peony; //ViewFactory2 model ViewFactoryModel2::ViewFactoryModel2(QObject *parent) : QAbstractListModel(parent) { } void ViewFactoryModel2::setDirectoryUri(const QString &uri) { beginResetModel(); m_support_views_id.clear(); m_factory_hash.clear(); m_current_uri = uri; auto viewManager = DirectoryViewFactoryManager2::getInstance(); auto defaultList = viewManager->getFactoryNames(); for (auto id : defaultList) { if (viewManager->getFactory(id)->supportUri(m_current_uri)) { QPair pair(viewManager->getFactory(id)->priority(m_current_uri), id); m_factory_hash.insert(viewManager->getFactory(id)->zoom_level_hint(), pair); m_support_views_id< m_support_views_id.count() - 1 || index < 0) { return nullptr; } return m_support_views_id.at(index); } int ViewFactoryModel2::rowCount(const QModelIndex &parent) const { // For list models only the root node (an invalid parent) should return the list's size. For all // other (valid) parents, rowCount() should return 0 so that it does not become a tree model. if (parent.isValid()) return 0; return m_support_views_id.count(); } const QString ViewFactoryModel2::getHighestPriorityViewId(int zoom_level_hint) { auto manager = DirectoryViewFactoryManager2::getInstance(); auto pairs = m_factory_hash.values(zoom_level_hint); DirectoryViewPluginIface2 *factory = nullptr; int priority = -9999; QString result; for (auto pair : pairs) { factory = manager->getFactory(pair.second); if (factory->priority(m_current_uri) > priority) { result = pair.second; priority = pair.first; } } return result; } QVariant ViewFactoryModel2::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); auto viewManager = DirectoryViewFactoryManager2::getInstance(); auto factory = viewManager->getFactory(viewManager->getFactoryNames().at(index.row())); switch (role) { case Qt::DecorationRole: return factory->viewIcon(); case Qt::ToolTipRole: return factory->viewIdentity(); case Qt::UserRole: return factory->viewIdentity(); default: break; } return QVariant(); } const QModelIndex ViewFactoryModel2::getIndexFromViewId(const QString &viewId) { if (!m_support_views_id.contains(viewId)) return QModelIndex(); return index(m_support_views_id.indexOf(viewId)); } peony/libpeony-qt/controls/tool-bar/search-bar.cpp0000644000175000017500000001335314205115226021173 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Yue Lan * Authors: Meihong He * */ #include "search-bar.h" #include #include #include #include #include #include #include using namespace Peony; SearchBar::SearchBar(FMWindow *window, QWidget *parent) : QLineEdit(parent) { m_top_window = window; init(window? true: false); } void SearchBar::init(bool hasTopWindow) { setTextMargins(5, 0, 0, 0); setFixedWidth(175); setToolTip(tr("Input the search key of files you would like to find.")); m_model = new QStandardItemModel(this); QCompleter *completer = new QCompleter(this); completer->setModel(m_model); completer->setMaxVisibleItems(10); //add two button in the completer, use QTableView m_table_view= new QTableView(this); m_table_view->setAttribute(Qt::WA_TranslucentBackground); //m_table_view->viewport()->setAttribute(Qt::WA_TranslucentBackground); m_table_view->setProperty("useCustomShadow", true); m_table_view->setProperty("customShadowDarkness", 0.5); m_table_view->setProperty("customShadowWidth", 20); m_table_view->setProperty("customShadowRadius", QVector4D(6, 6, 6, 6)); m_table_view->setProperty("customShadowMargins", QVector4D(20, 20, 20, 20)); m_table_view->setShowGrid(false); m_table_view->horizontalHeader()->setDefaultSectionSize(120); m_table_view->verticalHeader()->setDefaultSectionSize(6); m_table_view->horizontalHeader()->setVisible(false); m_table_view->verticalHeader()->setVisible(false); m_table_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_table_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); //m_table_view->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); initTableModel(); completer->setPopup(m_table_view); completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion); this->setCompleter(completer); //end completer setLayoutDirection(Qt::LeftToRight); setPlaceholderText(tr("Input search key...")); QAction *searchAction = addAction(QIcon::fromTheme("edit-find-symbolic"), QLineEdit::TrailingPosition); //NOTE: we should not add a short cut for line edit, //because it might have some bad effect for other controls. //use returnPressed signal trigger the action instead. //searchAction->setShortcut(Qt::Key_Return); connect(this, &QLineEdit::returnPressed, searchAction, &QAction::trigger); connect(this, &QLineEdit::textChanged, this, &SearchBar::searchKeyChanged); connect(m_table_view, SIGNAL(clicked(const QModelIndex &)), this, SLOT(onTableClicked(const QModelIndex &))); connect(searchAction, &QAction::triggered, this, &SearchBar::updateTableModel); } void SearchBar::focusInEvent(QFocusEvent *e) { blockSignals(false); QLineEdit::focusInEvent(e); this->completer()->complete(); } void SearchBar::focusOutEvent(QFocusEvent *e) { blockSignals(true); QLineEdit::focusOutEvent(e); //this->clear(); } void SearchBar::clearSearchRecord() { m_model->clear(); initTableModel(); QTimer::singleShot(100, this, [=]() { m_table_view->setVisible(false); }); } void SearchBar::initTableModel() { QStandardItem *advance = new QStandardItem(tr("advance search")); QStandardItem *clear = new QStandardItem(tr("clear record")); QList firstRow; firstRow<insertRow(0,firstRow); m_model->item(0)->setForeground(QBrush(QColor(10,10,255))); m_table_view->setMinimumHeight(25); m_table_view->setVisible(true); } void SearchBar::updateTableModel() { //comment unused code // if (!this->text().isEmpty()) { // bool contained = false; // for(int i=0; irowCount(); i++) // { // if(m_model->item(i)->text() == this->text()) // { // contained = true; // break; // } // } // if (! contained) // { // QStandardItem *key = new QStandardItem(this->text()); // QList row; // m_model->insertRow(0, row<setMinimumHeight(m_model->rowCount() * 25); // Q_EMIT this->searchRequest(this->text()); // this->clear(); // this->clearFocus(); // } } void SearchBar::onTableClicked(const QModelIndex &index) { qDebug()<<"onTableClicked"<rowCount()<text(); m_table_view->clearSelection(); if (index.row() != m_model->rowCount()-1) { this->setText(m_model->item(index.row())->text()); return; } if (index.column() == 0) { //clicked advance search m_top_window->advanceSearch(); //qDebug()<<"show or hidden advance filter"; } else if(index.column() == 1) { //clicked clear record clearSearchRecord(); } this->setText(""); } void SearchBar::hideTableView() { m_table_view->setVisible(false); } peony/libpeony-qt/controls/tool-bar/search-bar-container.h0000644000175000017500000000460414205115226022617 0ustar fengfeng/* * Peony-Qt * * Copyright (C) 2020, 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 . * * Authors: MeihongHe * */ #ifndef SEARCH_BAR_CONTAINER_H #define SEARCH_BAR_CONTAINER_H #include #include #include #include #include #include #include #include namespace Peony { class SearchBarContainer : public QWidget { Q_OBJECT public: explicit SearchBarContainer(QWidget *parent = nullptr); QSize sizeHint() const override; void setPlaceholderText(const QString &content) { m_search_box->setPlaceholderText(content); } void setFocus() { m_search_box->setFocus(); } QString text() { return m_search_box->text(); } void setText(QString text) { m_search_box->setText(text); } //get user selected index of file type int getFilterIndex() { return m_filter_box->currentIndex(); } void clearFilter() { m_filter_box->setCurrentIndex(0); } Q_SIGNALS: void returnPressed(); void filterUpdate(const int &index); public Q_SLOTS: void onTableClicked(const QModelIndex &index); void startSearch(); void clearSearchBox(); private: QHBoxLayout *m_layout = nullptr; QLineEdit *m_search_box; QComboBox *m_filter_box; QStringListModel *m_model = nullptr; QListView *m_list_view = nullptr; QTimer m_search_trigger; bool m_clear_action = true; QStringList m_file_type_list = {tr("all"), tr("file folder"), tr("image"), tr("video"), tr("text file"), tr("audio"), tr("wps file"), tr("others") }; }; } #endif // SEARCH_BAR_CONTAINER_H peony/libpeony-qt/controls/tool-bar/advance-search-bar.cpp0000644000175000017500000002270514205101223022563 0ustar fengfeng/* * Peony-Qt's Library * * Copyright (C) 2020, 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 . * * Authors: Meihong He * */ #include "advance-search-bar.h" #include "fm-window.h" #include "search-vfs-uri-parser.h" #include "file-utils.h" #include #include #include #include #include #include #include #include #include #include using namespace Peony; AdvanceSearchBar::AdvanceSearchBar(FMWindowIface *window, QWidget *parent) : QScrollArea(parent) { m_top_window = window; init(); } void AdvanceSearchBar::init() { //add multiple filter page //this->setBackgroundRole(QPalette::Light); m_filter = new QWidget(this); m_filter->setContentsMargins(0, 0, 0, 0); QLabel *keyLabel = new QLabel(tr("Key Words"), m_filter); m_advanced_key = new QLineEdit(m_filter); keyLabel->setBuddy(m_advanced_key); m_advanced_key->setPlaceholderText(tr("input key words...")); QLabel *searchLocation = new QLabel(tr("Search Location"), m_filter); m_search_path = new QLineEdit(m_filter); m_search_path->setPlaceholderText(tr("choose search path...")); QString uri = m_top_window->getCurrentUri(); //FIXME: replace BLOCKING api in ui thread. m_search_path->setText(FileUtils::getFileDisplayName(uri)); m_advance_target_path = uri; m_choosed_paths.push_back(uri); QPushButton *m_browse_button = new QPushButton(tr("browse"), nullptr); QLabel *fileType = new QLabel(tr("File Type"), m_filter); typeViewCombox = new QComboBox(m_filter); typeViewCombox->setToolTip(tr("Choose File Type")); auto model = new QStringListModel(m_filter); model->setStringList(m_file_type_list); typeViewCombox->setModel(model); QLabel *modifyTime = new QLabel(tr("Modify Time"), m_filter); timeViewCombox = new QComboBox(m_filter); timeViewCombox->setToolTip(tr("Choose Modify Time")); auto time_model = new QStringListModel(m_filter); time_model->setStringList(m_file_mtime_list); timeViewCombox->setModel(time_model); QLabel *fileSize = new QLabel(tr("File Size"), m_filter); sizeViewCombox = new QComboBox(m_filter); sizeViewCombox->setToolTip(tr("Choose file size")); auto size_model = new QStringListModel(m_filter); size_model->setStringList(m_file_size_list); sizeViewCombox->setModel(size_model); QPushButton *m_show_hidden_button = new QPushButton(tr("show hidden file"), nullptr); QPushButton *m_go_back = new QPushButton(tr("go back"), nullptr); m_go_back->setToolTip(tr("hidden advance search page")); QCheckBox *file_name = new QCheckBox(tr("file name"), nullptr); QCheckBox *file_content = new QCheckBox(tr("content"), nullptr); file_name->setChecked(true); m_search_content = false; m_search_name = true; QPushButton *m_filter_button = new QPushButton(tr("search"), nullptr); m_filter_button->setToolTip(tr("start search")); QFormLayout *topLayout = new QFormLayout(); topLayout->setContentsMargins(10, 10, 10, 10); QWidget *b1 = new QWidget(m_filter); QHBoxLayout *middleLayout = new QHBoxLayout(b1); b1->setLayout(middleLayout); QWidget *b2 = new QWidget(m_filter); QHBoxLayout *bottomLayout = new QHBoxLayout(b2); b2->setLayout(bottomLayout); QVBoxLayout *mainLayout = new QVBoxLayout(m_filter); mainLayout->addLayout(topLayout); mainLayout->addLayout(middleLayout); mainLayout->addLayout(bottomLayout); topLayout->addWidget(keyLabel); topLayout->addWidget(m_advanced_key); topLayout->addWidget(searchLocation); topLayout->addWidget(m_search_path); middleLayout->addWidget(m_browse_button, Qt::AlignCenter); middleLayout->setContentsMargins(10,10,10,10); topLayout->addWidget(b1); topLayout->addWidget(fileType); topLayout->addWidget(typeViewCombox); topLayout->addWidget(modifyTime); topLayout->addWidget(timeViewCombox); topLayout->addWidget(fileSize); topLayout->addWidget(sizeViewCombox); topLayout->addWidget(m_show_hidden_button); topLayout->addWidget(m_go_back); topLayout->addWidget(file_name); topLayout->addWidget(file_content); bottomLayout->setContentsMargins(10,20,10,10); bottomLayout->addWidget(m_filter_button, Qt::AlignCenter); topLayout->addWidget(b2); m_filter->setLayout(mainLayout); this->setWidget(m_filter); //end multiple filter //don't show HorizontalScroll setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); connect(m_browse_button, &QPushButton::clicked, this, &AdvanceSearchBar::browsePath); connect(m_filter_button, &QPushButton::clicked, this, &AdvanceSearchBar::searchFilter); connect(m_show_hidden_button, &QPushButton::clicked, this, &AdvanceSearchBar::setShowHidden); connect(m_search_path, &QLineEdit::textChanged, this, &AdvanceSearchBar::pathChanged); connect(typeViewCombox, &QComboBox::currentTextChanged,this, &AdvanceSearchBar::filterUpdate); connect(timeViewCombox, &QComboBox::currentTextChanged,this, &AdvanceSearchBar::filterUpdate); connect(sizeViewCombox, &QComboBox::currentTextChanged,this, &AdvanceSearchBar::filterUpdate); connect(file_name, &QCheckBox::clicked, this, [=]() { m_search_name = file_name->isChecked(); qDebug()<<"search name"<isChecked(); qDebug()<<"search content"<